[英]How to map a map JSON column to Java Object with JPA
我们有一张有很多列的大桌子。 迁移到 MySQL Cluster 后,无法创建表,原因是:
错误 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;
}
通过使用它,我们可以将所有列合并为一个并创建我们的表。 或者我们可以将整个表拆分成几个表。 我个人更喜欢第一种解决方案。
无论如何,我的问题是如何映射作为文本并包含 Java 对象的 JSON 字符串的 Params 列?
您可以使用 JPA 转换器将您的实体映射到数据库。 只需在您的 params 字段中添加一个与此类似的注释:
@Convert(converter = JpaConverterJson.class)
然后以类似的方式创建类(这将转换一个通用对象,您可能需要专门化它):
@Converter(autoApply = true)
public class JpaConverterJson implements AttributeConverter<Object, String> {
private final static ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Object meta) {
try {
return objectMapper.writeValueAsString(meta);
} catch (JsonProcessingException ex) {
return null;
// or throw an error
}
}
@Override
public Object convertToEntityAttribute(String dbData) {
try {
return objectMapper.readValue(dbData, Object.class);
} catch (IOException ex) {
// logger.error("Unexpected IOEx decoding json from database: " + dbData);
return null;
}
}
}
就是这样:您可以使用此类将任何对象序列化为表中的 json。
JPA AttributeConverter
对映射 JSON 对象类型的限制太多,尤其是当您想将它们保存为 JSON 二进制文件时。
您不必创建自定义 Hibernate Type 来获得 JSON 支持,您需要做的就是使用Hibernate Types OSS 项目。
例如,如果您使用的是 Hibernate 5.2 或更新版本,那么您需要在 Maven
pom.xml
配置文件中添加以下依赖项:<dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency>
现在,您需要在实体属性级别或更好地在基类中的类级别使用@MappedSuperclass
声明新类型:
@TypeDef(name = "json", typeClass = JsonType.class)
实体映射将如下所示:
@Type(type = "json")
@Column(columnDefinition = "json")
private Location location;
如果您使用的是 Hibernate 5.2 或更高版本,那么MySQL57Dialect
自动注册JSON
类型。
否则,您需要自己注册:
public class MySQLJsonDialect extends MySQL55Dialect {
public MySQLJsonDialect() {
super();
this.registerColumnType(Types.JAVA_OBJECT, "json");
}
}
并且,将hibernate.dialect
Hibernate 属性设置为使用您刚刚创建的MySQLJsonDialect
类的完全限定类名。
如果您在响应客户端时需要将 json 类型属性映射到 json 格式(例如 rest API 响应),请添加@JsonRawValue
如下:
@Column(name = "params", columnDefinition = "json")
@JsonRawValue
private String params;
这可能不会为服务器端使用做 DTO 映射,但客户端会得到正确格式化为 json 的属性。
对于那些不想编写太多代码的人,有一种解决方法。
前端 -> 在 POST 方法中将您的 JSON 对象编码为字符串 base64,在 GET 方法中将其解码为 json
In POST Method
data.components = btoa(JSON.stringify(data.components));
In GET
data.components = JSON.parse(atob(data.components))
Backend -> 在您的 JPA 代码中,将列更改为 String 或 BLOB,无需转换。
@Column(name = "components", columnDefinition = "json")
private String components;
我有一个类似的问题,并通过使用 @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<String, Object> map;
public Params () {
this.map = new HashMap<String, Object>();
}
public Params (Params another) {
this.map = new HashMap<String, Object>();
this.map.putAll(anotherHolder.map);
}
public Params(String string) {
try {
TypeReference<Map<String, Object>> typeRef = new TypeReference<Map<String, Object>>() {
};
if (string == null) {
this.map = new HashMap<String, Object>();
} else {
this.map = mapper.readValue(string, typeRef);
}
} catch (IOException e) {
throw new PersistenceException(e);
}
}
public String toJSON() throws PersistenceException {
try {
return mapper.writeValueAsString(this.map);
} catch (IOException e) {
throw new PersistenceException(e);
}
}
public boolean containsKey(String key) {
return this.map.containsKey(key);
}
// Hash map methods
public Object get(String key) {
return this.map.get(key);
}
public Object put(String key, Object value) {
return this.map.put(key, value);
}
public void remove(String key) {
this.map.remove(key);
}
public Object size() {
return map.size();
}
}
HTH
为什么我们真正使用 JSON / BLOB ?
相反,如果您在服务器端使用了过多的连接并再次使用 blob,则意味着设计不佳
在这个较新版本的 Spring Boot 和 MySQL 中,下面的代码就足够了
@Column( columnDefinition = "json" )
private String string;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.