错误:列的类型为 json,但表达式的类型为 Hibernate 中不同的字符类型

问题描述 投票:0回答:5

我需要使用 spring data jpa 将实体类的两列映射为 postgres 中的 json。在阅读了多篇 stackoverflow 帖子和 baeldung 帖子后,

如何使用 JPA 将映射 JSON 列映射到 Java 对象

https://www.baeldung.com/hibernate-persist-json-object

我做了如下配置。但是,我遇到错误“错误:列“标题”的类型为 json,但表达式的类型为字符变化

请提供一些解决此问题的指示。

我有一个实体类如下

@Entity
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
public class Task {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;
    
    private String url;
    private String httpMethod;

    @Convert(converter = HashMapConverter.class)
    @Column(columnDefinition = "json")
    private Map<String, String> headers;

    @Convert(converter = HashMapConverter.class)
    @Column(columnDefinition = "json")
    private Map<String, String> urlVariables;
}

我创建了一个测试类来测试实体是否持久化。运行此 junit 时,下面的测试用例失败并出现错误,如下所示

@SpringBootTest
class TaskRepositoryTest {

    private static Task randomTask = randomTask();

    @Autowired
    private TaskRepository taskRepository;

    @BeforeEach
    void setUp() {
        taskRepository.deleteAll();
        taskRepository.save(randomTask);
    }

    public static Task randomTask() {
        return randomTaskBuilder().build();
    }

    public static TaskBuilder randomTaskBuilder() {
        Map<String,String> headers = new HashMap<>();
        headers.put(randomAlphanumericString(10),randomAlphanumericString(10));

        Map<String,String> urlVariables = new HashMap<>();
        urlVariables.put(randomAlphanumericString(10),randomAlphanumericString(10));

        return builder()
                .id(randomPositiveInteger())
                .httpMethod(randomAlphanumericString(10))
                .headers(headers)
                .urlVariables(urlVariables)
                .url(randomAlphanumericString(10)));
    }
}

使用 liquibase,我在 postgres DB 中创建了表,我可以看到列数据类型为 json。

databaseChangeLog:
  - changeSet:
      id: 1
      author: abc
      changes:
        - createTable:
            tableName: task
            columns:
              - column:
                  name: id
                  type: int
                  autoIncrement: true
                  constraints:
                    primaryKey: true
              - column:
                  name: url
                  type: varchar(250)
                  constraints:
                    nullable: false
                    unique: true
              - column:
                  name: http_method
                  type: varchar(50)
                  constraints:
                    nullable: false
              - column:
                  name: headers
                  type: json
              - column:
                  name: url_variables
                  type: json
      rollback:
        - dropTable:
            tableName: task
json postgresql spring-boot spring-data-jpa liquibase
5个回答
14
投票

使用

org.hibernate.annotations.ColumnTransformer

的替代解决方案

示例实体

import org.hibernate.annotations.ColumnTransformer;

@Entity
@Table
public class SomeEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.SEQUENCE)
  private Long id;

  @Column(name = "some_class", columnDefinition = "jsonb")
  @Convert(converter = SomeClassConvertor.class)
  @ColumnTransformer(write = "?::jsonb")
  private SomeClass someClass;
}

转换器示例

import java.io.IOException;
import javax.persistence.AttributeConverter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.airtel.africa.entity.SomeClass;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class SomeClassConvertor implements AttributeConverter<SomeClass, String> {
  @Autowired
  ObjectMapper objectMapper;

  @Override
  public String convertToDatabaseColumn(SomeClass someClass) {
    String someClassJson = null;
    try {
      someClassJson = objectMapper.writeValueAsString(someClass);
    } catch (final JsonProcessingException e) {
      log.error("JSON writing error", e);
    }

    return someClassJson;
  }

  @Override
  public SomeClass convertToEntityAttribute(String someClassJSON) {
    SomeClass someClass = null;
    if (StringUtils.isBlank(someClassJSON)) {
      return someClass;
    }
    try {
      someClass = objectMapper.readValue(someClassJSON, someClass.class);
    } catch (final IOException e) {
      log.error("JSON reading error", e);
    }
    return someClass;
  }
}


14
投票

对于因为使用

JdbcTemplate
并收到此错误而登陆此处的任何人,解决方案非常简单:在 SQL 语句中,使用
::jsonb
或 CAST 转换 JSON 参数。

例如

String INSERT_SQL = "INSERT INTO xxx (id, json_column) VALUES(?, ?)";

变成

String INSERT_SQL = "INSERT INTO xxx (id, json_column) VALUES(?, ?::jsonb)";

或使用命名参数

String INSERT_SQL = "INSERT INTO xxx (id, json_column) VALUES(:id, CAST(:json_column AS JSONB))";


8
投票

以上配置不起作用。

因此,我按照以下链接来解决用例

https://vladmihalcea.com/how-to-map-json-objects-using-generic-hibernate-types/

Spring Boot 升级后

“未找到提供程序 com.fasterxml.jackson.module.jaxb.JaxbAnnotationModule”

在 pom.xml 中添加了额外的依赖项

        <dependency>
            <groupId>com.fasterxml.jackson.module</groupId>
            <artifactId>jackson-module-jaxb-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>com.vladmihalcea</groupId>
            <artifactId>hibernate-types-52</artifactId>
            <version>2.9.11</version>
        </dependency>

删除了 HashMapConverter 配置并在实体类中进行了以下更改

@Entity
@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
@TypeDefs({
        @TypeDef(name = "json", typeClass = JsonStringType.class),
        @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class Task {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;
    
    private String url;
    private String httpMethod;

    @Type(type = "jsonb")
    @Column(columnDefinition = "json")
    private Map<String, String> headers;

    @Type(type = "jsonb")
    @Column(columnDefinition = "json")
    private Map<String, String> urlVariables;
}

这些更改之后,TaskRepositoryTest 通过了。


5
投票

当我将项目从 MySQL 8.0.21 迁移到 Postgres 13 时,我遇到了这个问题。我的项目使用 Spring boot 和 Hibernate 类型依赖项版本 2.7.1。就我而言,解决方案很简单。

我所需要做的就是改变它,它就起作用了。

引用自 Hibernate 类型文档页面


3
投票

(对于后代)有一个替代解决方案,并不完全安全(自动从字符串转换为字符串),但对于您控制的非关键或数据/结构来说应该没问题:

将 ?stringtype=unspecified 添加到你的 postgresql 连接字符串中:

例如: (在您的 application.yml 中)

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/dbname?stringtype=unspecified

这将使 postgresql 尝试自动将字符串转换为 json。 不需要任何额外的导入或 @type 定义或自定义 sql 查询,问题中的示例就是您所需要的:

@Convert(converter = HashMapConverter.class)
@Column(columnDefinition = "json")
private Map<String, String> headers;

PPS:我还没有尝试过使用 jsonb 或其他数据库供应商。

© www.soinside.com 2019 - 2024. All rights reserved.