如何在Liquibase与JPA实体类中正确配置JSON数据列?
问题:如何在Liquibase XML和JPA实体类中配置JSON类型列?
我想要创建一张包含名为data的JSON数据存储列的表,请问如何在Liquibase的XML文件中配置该列,以及如何在JPA实体类中进行对应编写?我目前的写法如下,但不确定是否正确:
Liquibase XML 代码:
<changeSet author="ABC" id="HUI"> <createTable tableName="archived_table"> <column autoIncrement="true" name="id" type="BIGINT"> <constraints primaryKey="true" primaryKeyName="archive_tablePK" /> </column> <column name="created" type="TIMESTAMP(6)"> </column> <column name="data" type="jsonb"/> </createTable> </changeSet>
JPA 实体类代码:
@Entity @Table(name = "archived_table") public class ArchiveModel extends BaseModel{ @Column(nullable = false) private JSONObject data; public ArchiveDataModel( JSONObject data, ){ super(); this.data=data; } public JSONObject getData() { return data; } public void setData(JSONObject data) { this.data = data; } }
回答
一、Liquibase XML 配置部分
你的Liquibase配置基本正确,但有几个细节可以优化:
jsonb是PostgreSQL专属的JSON类型,如果你用的是MySQL需要改成json,Oracle则用JSON,要根据数据库类型对应调整type属性值。- 如果业务要求
data列非空,建议在Liquibase中添加约束,和实体类的nullable=false对应:
<column name="data" type="jsonb"> <constraints nullable="false"/> </column>
created列建议添加默认值(当前时间),更贴合业务逻辑:
<column name="created" type="TIMESTAMP(6)" defaultValueComputed="CURRENT_TIMESTAMP(6)"> </column>
二、JPA 实体类部分
你的实体类写法存在关键问题,直接使用JSONObject(不管是Fastjson还是Jackson的实现)无法被JPA正确持久化,需要做以下调整:
方案1:自定义JPA属性转换器
适合需要指定JSON库处理序列化/反序列化的场景:
- 先创建转换器类:
import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.persistence.AttributeConverter; import jakarta.persistence.Converter; import java.io.IOException; @Converter(autoApply = true) public class JsonObjectConverter implements AttributeConverter<Object, String> { private static final ObjectMapper objectMapper = new ObjectMapper(); @Override public String convertToDatabaseColumn(Object attribute) { try { return objectMapper.writeValueAsString(attribute); } catch (IOException e) { throw new IllegalArgumentException("无法将对象转为JSON字符串", e); } } @Override public Object convertToEntityAttribute(String dbData) { try { return objectMapper.readValue(dbData, Object.class); } catch (IOException e) { throw new IllegalArgumentException("无法将JSON字符串转为对象", e); } } }
- 修改实体类:
@Entity @Table(name = "archived_table") public class ArchiveModel extends BaseModel { @Column(nullable = false, columnDefinition = "jsonb") @Convert(converter = JsonObjectConverter.class) private Object data; // 也可以用Map<String, Object>或者自定义DTO类 // 构造方法名必须和实体类名一致,你的代码里写的ArchiveDataModel是错误的 public ArchiveModel(Object data) { super(); this.data = data; } // JPA要求实体类必须有无参构造方法 public ArchiveModel() { super(); } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
方案2:用Hibernate原生JSON支持(基于Hibernate作为JPA实现)
Hibernate对JSON类型有原生支持,无需自定义转换器:
import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; @Entity @Table(name = "archived_table") public class ArchiveModel extends BaseModel { @Column(nullable = false, columnDefinition = "jsonb") @JdbcTypeCode(SqlTypes.JSON) private Object data; // 可替换为Map<String, Object>或自定义DTO // 修正构造方法名,添加JPA必需的无参构造 public ArchiveModel() { super(); } public ArchiveModel(Object data) { super(); this.data = data; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } }
实体类的核心错误修正:
- 构造方法名错误:你写的
ArchiveDataModel必须和实体类名ArchiveModel一致。 - 缺少无参构造:JPA强制要求实体类提供无参构造方法。
- 直接使用
JSONObject无法被JPA正确映射,必须通过转换器或Hibernate原生注解处理序列化逻辑。
内容的提问来源于stack exchange,提问作者Umar
相关产品推荐
相关产品推荐

