问题 如何在H2中解决JSON列


我在应用程序MySQL 5.7中使用,我有JSON列。当我尝试运行我的集成测试时不起作用,因为H2数据库无法创建表。这是错误:

2016-09-21 16:35:29.729 ERROR 10981 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : HHH000389: Unsuccessful: create table payment_transaction (id bigint generated by default as identity, creation_date timestamp not null, payload json, period integer, public_id varchar(255) not null, state varchar(255) not null, subscription_id_zuora varchar(255), type varchar(255) not null, user_id bigint not null, primary key (id))
2016-09-21 16:35:29.730 ERROR 10981 --- [           main] org.hibernate.tool.hbm2ddl.SchemaExport  : Unknown data type: "JSON"; SQL statement:

这是实体类。

@Table(name = "payment_transaction")
public class PaymentTransaction extends DomainObject implements Serializable {

    @Convert(converter = JpaPayloadConverter.class)
    @Column(name = "payload", insertable = true, updatable = true, nullable = true, columnDefinition = "json")
    private Payload payload;

    public Payload getPayload() {
        return payload;
    }

    public void setPayload(Payload payload) {
        this.payload = payload;
    }
}

而子类:

public class Payload implements Serializable {

    private Long userId;
    private SubscriptionType type;
    private String paymentId;
    private List<String> ratePlanId;
    private Integer period;

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public SubscriptionType getType() {
        return type;
    }

    public void setType(SubscriptionType type) {
        this.type = type;
    }

    public String getPaymentId() {
        return paymentId;
    }

    public void setPaymentId(String paymentId) {
        this.paymentId = paymentId;
    }

    public List<String> getRatePlanId() {
        return ratePlanId;
    }

    public void setRatePlanId(List<String> ratePlanId) {
        this.ratePlanId = ratePlanId;
    }

    public Integer getPeriod() {
        return period;
    }

    public void setPeriod(Integer period) {
        this.period = period;
    }

}

并且这个转换器用于插入数据库:

public class JpaPayloadConverter implements AttributeConverter<Payload, String> {

    // ObjectMapper is thread safe
    private final static ObjectMapper objectMapper = new ObjectMapper();

    private Logger log = LoggerFactory.getLogger(getClass());

    @Override
    public String convertToDatabaseColumn(Payload attribute) {
        String jsonString = "";
        try {
            log.debug("Start convertToDatabaseColumn");

            // convert list of POJO to json
            jsonString = objectMapper.writeValueAsString(attribute);
            log.debug("convertToDatabaseColumn" + jsonString);

        } catch (JsonProcessingException ex) {
            log.error(ex.getMessage());
        }
        return jsonString;
    }

    @Override
    public Payload convertToEntityAttribute(String dbData) {

        Payload payload = new Payload();
        try {
            log.debug("Start convertToEntityAttribute");

            // convert json to list of POJO
            payload = objectMapper.readValue(dbData, Payload.class);
            log.debug("JsonDocumentsConverter.convertToDatabaseColumn" + payload);

        } catch (IOException ex) {
            log.error(ex.getMessage());
        }
        return payload;

    }
}

6900
2017-09-21 15:09


起源

确实克服了这个问题?我也试图做类似的事情,但没有成功 - nadavgam


答案:


H2没有JSON数据类型。

JSON本质上只是一个非常长的字符串,因此您可以使用大多数数据库中可用的CLOB。

只有在需要对其进行操作的SQL函数时,才需要行级的JSON类型,并且只有当数据库坚持其JSON函数在JSON类型而不是CLOB上运行时才需要。


5
2018-03-14 13:17





我刚刚遇到了这个问题 JSONB 列类型 - 的二进制版本 JSON 类型,不映射到 TEXT

为了将来参考,您可以使用在H2中定义自定义类型 CREATE DOMAIN, 如下:

CREATE domain IF NOT EXISTS jsonb AS other;

这似乎对我有用,并允许我成功测试我的代码对实体。

资源: https://objectpartners.com/2015/05/26/grails-postgresql-9-4-and-jsonb/


5
2018-06-13 12:50





我在H2中使用TEXT类型解决了这个问题。 必须创建一个单独的数据库脚本,以便在H2中为测试创建模式,并用TEXT替换JSON类型。

这仍然是一个问题,因为如果你在查询中使用Json函数,你将无法在使用H2时测试它们。


2
2017-09-07 17:46



不幸的是,我将不得不使用其他东西,因为我使用的是Spring @Type注释,如果我将JSON类型更改为TEXT或CLOB为liquibase changelog文件,则会失败。 - Alex
如果您使用不同的数据库类型(如H2),我建议您为测试停用hibernate表创建。使用spring配置文件停用hibernate auto create并使用liquibase配置文件为测试环境使用不同的脚本。 - Olivier Garand
您还可以使用具有不同配置文件的Spring配置来管理测试实体与prod实体,并仍然使用@Type和hibernate auto create。然后,您必须在不同的包中创建“测试”实体。在我看来,这是一个糟糕的设计,它可能是矫枉过正的,我会个人停用autocreate并使用不同liquibase配置文件的自定义liquibase脚本。在我自己的观点和经验中,Hibernate表管理过于僵化,无法与多个数据库一起使用。 - Olivier Garand
感谢您的回复:)我设法用EmbeddedPostgreSQL替换H2来运行测试。启动速度有点慢,但它会阻止我自己解决未来的问题。 Liquibase不是100%DB不可知的,他们做得很好,但在尝试使用Oracle / Postgres开关和Postgres / H2时我遇到了一些问题。 - Alex