JPA将JSON列映射到Java Object
我们有一个包含大量列的大表。 在我们转移到MySQL Cluster之后,由于以下原因无法创建表:
ERROR 1118(42000):行大小太大。 所使用的表类型的最大行大小(不包括BLOB)是14000.这包括存储开销,请查看手册。 您必须将某些列更改为TEXT或BLOB
举个例子:
@Entity @Table (name = "appconfigs", schema = "myproject") public class AppConfig implements Serializable { @Id @Column (name = "id", nullable = false) @GeneratedValue (strategy = GenerationType.IDENTITY) private int id; @OneToOne @JoinColumn (name = "app_id") private App app; @Column(name = "param_a") private ParamA parama; @Column(name = "param_b") private ParamB paramb; }
它是用于存储配置参数的表。 我想我们可以将一些列组合成一个并将其存储为JSON对象并将其转换为某个Java对象。
例如:
@Entity @Table (name = "appconfigs", schema = "myproject") public class AppConfig implements Serializable { @Id @Column (name = "id", nullable = false) @GeneratedValue (strategy = GenerationType.IDENTITY) private int id; @OneToOne @JoinColumn (name = "app_id") private App app; @Column(name = "params") //How to specify that this should be mapped to JSON object? private Params params; }
我们定义的地方:
public class Params implements Serializable { private ParamA parama; private ParamB paramb; }
通过使用它,我们可以将所有列合并为一个并创建我们的表。 或者我们可以将整个表分成几个表。 我个人更喜欢第一种解决方案。
无论如何我的问题是如何映射Params列,它是文本并包含Java对象的JSON字符串?
您可以使用JPA转换器将实体映射到数据库。 只需在params字段中添加与此类似的注释:
@Convert(converter = JpaConverterJson.class)
然后以类似的方式创建类(这会转换一个通用的Object,你可能想要专门化它):
@Converter(autoApply = true) public class JpaConverterJson implements AttributeConverter
就是这样:您可以使用此类将任何对象序列化为表中的json。
正如我在本文中解释的那样,JPA AttributeConverter
太局限于映射JSON对象类型,特别是如果要将它们保存为JSON二进制文件。
您不必手动创建所有这些类型,只需使用以下依赖项通过Maven Central获取它们:
com.vladmihalcea hibernate-types-52 ${hibernate-types.version} 有关更多信息,请查看hibernate类型的开源项目 。
现在,解释它是如何工作的。
我写了一篇关于如何在PostgreSQL和MySQL上映射JSON对象的文章 。
对于PostgreSQL,您需要以二进制forms发送JSON对象:
public class JsonBinaryType extends AbstractSingleColumnStandardBasicType
JsonBinarySqlTypeDescriptor
如下所示:
public class JsonBinarySqlTypeDescriptor extends AbstractJsonSqlTypeDescriptor { public static final JsonBinarySqlTypeDescriptor INSTANCE = new JsonBinarySqlTypeDescriptor(); @Override public ValueBinder getBinder( final JavaTypeDescriptor javaTypeDescriptor) { return new BasicBinder (javaTypeDescriptor, this) { @Override protected void doBind( PreparedStatement st, X value, int index, WrapperOptions options) throws SQLException { st.setObject(index, javaTypeDescriptor.unwrap( value, JsonNode.class, options), getSqlType() ); } @Override protected void doBind( CallableStatement st, X value, String name, WrapperOptions options) throws SQLException { st.setObject(name, javaTypeDescriptor.unwrap( value, JsonNode.class, options), getSqlType() ); } }; } }
和JsonTypeDescriptor
这样:
public class JsonTypeDescriptor extends AbstractTypeDescriptor
现在,您需要在类级别或package-info.java包级别描述符中声明新类型:
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
实体映射将如下所示:
@Type(type = "jsonb") @Column(columnDefinition = "json") private Location location;
如果您使用的是Hibernate 5或更高版本,则Postgre92Dialect会自动注册 JSON
类型。
否则,您需要自己注册:
public class PostgreSQLDialect extends PostgreSQL91Dialect { public PostgreSQL92Dialect() { super(); this.registerColumnType( Types.JAVA_OBJECT, "json" ); } }
我有一个类似的问题,并通过使用@Externalizer注释和Jackson解决它来序列化/反序列化数据(@Externalizer是OpenJPA特定的注释,所以你必须检查你的JPA实现类似的可能性)。
@Persistent @Column(name = "params") @Externalizer("toJSON") private Params params;
参数类实现:
public class Params { private static final ObjectMapper mapper = new ObjectMapper(); private Map map; public Params () { this.map = new HashMap(); } public Params (Params another) { this.map = new HashMap(); this.map.putAll(anotherHolder.map); } public Params(String string) { try { TypeReference
HTH