使用 Hibernate 和 JPA 持久化 JSON 对象
Posted
技术标签:
【中文标题】使用 Hibernate 和 JPA 持久化 JSON 对象【英文标题】:Persisting a JSON Object using Hibernate and JPA 【发布时间】:2017-04-09 17:12:33 【问题描述】:我正在尝试在 Spring Boot 中将 JSON 对象存储在 mysql 数据库中。我知道我做错了什么,但我无法弄清楚它是什么,因为我对 Spring 还很陌生。
我有一个休息端点,我在其中获取以下 JSON 对象(通过 HTTP PUT),我需要将它存储在数据库中,以便用户稍后可以获取它(通过 HTTP GET)。
"A":
"Name": "Cat",
"Age": "1"
,
"B":
"Name": "Dog",
"Age": "2"
,
"C":
"Name": "Horse",
"Age": "1"
请注意,在上述情况下,对象中的 number of keys 可能会有所不同,由于该要求,我使用的是 @987654322 @ 在控制器中捕获对象。
@RequestMapping(method = RequestMethod.POST)
public String addPostCollection(@RequestBody HashMap<String, Animal> hp)
hp.forEach((x, y) ->
postRepository.save(hp.get(x));
);
return "OK";
正如您在方法中看到的,我可以迭代 HashMap
并将每个 Animal
对象保存在 db 中。但我正在寻找一种将整个HashMap
保存在一条记录中的方法。我做了一些阅读,他们建议我使用@ManyToMany
映射。
谁能指出我以不同方式坚持HashMap
的方向? (或者是使用@ManyToMany
唯一且正确的方法吗?)
【问题讨论】:
"整个 HashMap 在一条记录中" ??还是单个数据库事务中的整个哈希图? 我的意思是单条记录。 【参考方案1】:Maven 依赖
你需要做的第一件事是在你的项目pom.xml
配置文件中设置如下Hibernate TypesMaven依赖:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>$hibernate-types.version</version>
</dependency>
领域模型
假设您有以下实体:
@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
typeClass = JsonType.class,
defaultForType = JsonNode.class
)
public class Book
@Id
@GeneratedValue
private Long id;
@NaturalId
private String isbn;
@Column(columnDefinition = "jsonb")
private JsonNode properties;
//Getters and setters omitted for brevity
请注意,@TypeDef
用于指示 Hibernate 使用 Hibernate Types 项目提供的 JsonType
映射 JsonNode
对象。
测试时间
现在,如果你保存一个实体:
Book book = new Book();
book.setIsbn( "978-9730228236" );
book.setProperties(
JacksonUtil.toJsonNode(
"" +
" \"title\": \"High-Performance Java Persistence\"," +
" \"author\": \"Vlad Mihalcea\"," +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99" +
""
)
);
entityManager.persist( book );
Hibernate 将生成以下 SQL 语句:
INSERT INTO
book
(
isbn,
properties,
id
)
VALUES
(
'978-9730228236',
'"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99',
1
)
你也可以加载回来修改:
Session session = entityManager.unwrap( Session.class );
Book book = session
.bySimpleNaturalId( Book.class )
.load( "978-9730228236" );
LOGGER.info( "Book details: ", book.getProperties() );
book.setProperties(
JacksonUtil.toJsonNode(
"" +
" \"title\": \"High-Performance Java Persistence\"," +
" \"author\": \"Vlad Mihalcea\"," +
" \"publisher\": \"Amazon\"," +
" \"price\": 44.99," +
" \"url\": \"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/\"" +
""
)
);
Hibernate 为您处理 UPDATE
声明:
SELECT b.id AS id1_0_
FROM book b
WHERE b.isbn = '978-9730228236'
SELECT b.id AS id1_0_0_ ,
b.isbn AS isbn2_0_0_ ,
b.properties AS properti3_0_0_
FROM book b
WHERE b.id = 1
-- Book details: "price":44.99,"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon"
UPDATE
book
SET
properties = '"title":"High-Performance Java Persistence","author":"Vlad Mihalcea","publisher":"Amazon","price":44.99,"url":"https://www.amazon.com/High-Performance-Java-Persistence-Vlad-Mihalcea/dp/973022823X/"'
WHERE
id = 1
【讨论】:
感谢您的提示!但是,您可以使用不知道任何 JSON 类型的 H2 数据库来完成这项工作吗?我使用 Spring Boot 和 H2 进行测试。也许以某种方式覆盖 H2 方言以将 JSON 类型视为文本?但不知道如何......谢谢 @VladMihalcea 我的集成测试确实使用 MySQL 数据库,但使用 MySQL 进行 JPA 单元测试会很痛苦和荒谬。【参考方案2】:您可以使用 FasterXML(或类似的)将 Json 解析为实际对象(您需要定义类)并使用 Json.toJson(yourObj).toString()
检索 Json 字符串。它还简化了对对象的处理,因为您的数据类也可能具有功能。
【讨论】:
【参考方案3】:您的 JSON 结构良好,因此通常无需将整个地图保存在一条记录中。您将无法使用 Hibernate/JPA 查询功能等等。
如果您真的想将整个地图保存在一条记录中,您可以将地图保存在其字符串表示中,并且如前所述,使用 JSON 解析器(如 Jackson)来重建您的 HashMap
@Entity
public class Animals
private String animalsString;
public void setAnimalsString(String val)
this.animalsString = val;
public String getAnimalsString()
return this.animalsMap;
public HashMap<String, Animal> getAnimalsMap()
ObjectMapper mapper = new ObjectMapper();
TypeReference<HashMap<String,Animal>> typeRef = new TypeReference<HashMap<String,Animal>>() ;
return mapper.readValue(animalsString, typeRef);
你的动物类:
public class Animal
private String name;
private int age;
/* getter and setter */
/* ... */
您可以将控制器方法更改为
@RequestMapping(method = RequestMethod.POST)
public String addPostCollection(@RequestBody String hp)
Animals animals = new Animals();
animals.setAnimalsString(hp);
animalsRepository.save(hp);
return "OK";
【讨论】:
我认为这对我来说是最好的选择。将对象保存为字符串并将其映射到地图。【参考方案4】:一种动物就是一种记录。您正在保存更多记录,而不是一条记录。您可以在一个事务中提交更多记录。 见:How to persist a lot of entities (JPA)
【讨论】:
以上是关于使用 Hibernate 和 JPA 持久化 JSON 对象的主要内容,如果未能解决你的问题,请参考以下文章
在 JPA/Hibernate 中持久化映射到 MySQL 视图的实体
JPASpringData JPA Hibernate和Mybatis 的区别和联系