如何使用通用的Hibernate类型映射JSON对象
介绍
在本文中,我们将看到如何使用Hibernate Types开源项目将JSON列映射到JPA实体属性。
虽然您可以创建自己的自定义Hibernate类型,但可以在Oracle,SQL Server,PostgreSQL或MySQL上映射JSON列类型,但是由于Hibernate Types项目已经提供了此功能,因此您无需实现自己的Hibernate Type 。
领域模型
假设我们具有以下域模型:

Location和Ticket是JSON Object(s),而Event和Participant是JPA实体。我们的目标是提供一种Type适用于任何类型的JSON JavaObject以及支持JSON列的任何关系数据库的Hibernate JSON 。
Maven依赖
您需要做的第一件事是在项目pom.xml配置文件中设置以下Maven依赖项:
1 2 3 4 5
| <dependency> <groupId>com.vladmihalcea</groupId> <artifactId>hibernate-types-52</artifactId> <version>${hibernate-types.version}</version> </dependency>
|
如果您使用的是Hibernate的旧版本,请查看hibernate-typesGitHub存储库
,以获取有关当前Hibernate版本的匹配依赖项的更多信息。
声明Hibernate Types
要使用JSON Hibernate Types,我们必须使用@TypeDef注释声明它们:
1 2 3 4 5 6 7 8
| @TypeDefs({ @TypeDef(name = "json", typeClass = JsonStringType.class), @TypeDef(name = "jsonb", typeClass = JsonBinaryType.class) }) @MappedSuperclass public class BaseEntity { }
|
TypeDef批注可以应用于基本实体类,也可以应用于与当前实体包关联的package-info.java文件。
MySQL
MySQL 5.7增加了对JSON类型的支持,在JDBC级别上,需要将其实现为String。因此,我们将使用JsonStringType。
实体映射如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| @Entity(name = "Event") @Table(name = "event") public class Event extends BaseEntity { @Type(type = "json") @Column(columnDefinition = "json") private Location location; public Location getLocation() { return location; } public void setLocation(Location location) { this.location = location; } } @Entity(name = "Participant") @Table(name = "participant") public class Participant extends BaseEntity { @Type(type = "json") @Column(columnDefinition = "json") private Ticket ticket; @ManyToOne private Event event; public Ticket getTicket() { return ticket; } public void setTicket(Ticket ticket) { this.ticket = ticket; } public Event getEvent() { return event; } public void setEvent(Event event) { this.event = event; } }
|
插入以下实体时:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| final AtomicReference<Event> eventHolder = new AtomicReference<>(); final AtomicReference<Participant> participantHolder = new AtomicReference<>(); doInJPA(entityManager -> { Event nullEvent = new Event(); nullEvent.setId(0L); entityManager.persist(nullEvent); Location location = new Location(); location.setCountry("Romania"); location.setCity("Cluj-Napoca"); Event event = new Event(); event.setId(1L); event.setLocation(location); entityManager.persist(event); Ticket ticket = new Ticket(); ticket.setPrice(12.34d); ticket.setRegistrationCode("ABC123"); Participant participant = new Participant(); participant.setId(1L); participant.setTicket(ticket); participant.setEvent(event); entityManager.persist(participant); eventHolder.set(event); participantHolder.set(participant); });
|
Hibernate生成以下语句:
1 2 3 4 5 6 7 8
| INSERT INTO event (location, id) VALUES (NULL(OTHER), 0) INSERT INTO event (location, id) VALUES ('{"country":"Romania","city":"Cluj-Napoca"}', 1) INSERT INTO participant (event_id, ticket, id) VALUES (1, {"registrationCode":"ABC123","price":12.34}, 1)
|
JSONObject(s)已正确实现在其关联的数据库列中。
不仅JSONObject(s)已从其数据库表示形式正确转换:
1 2 3 4 5 6 7
| Event event = entityManager.find(Event.class, eventHolder.get().getId()); assertEquals("Cluj-Napoca", event.getLocation().getCity()); Participant participant = entityManager.find( Participant.class, participantHolder.get().getId()); assertEquals("ABC123", participant.getTicket().getRegistrationCode());
|
但是我们甚至可以发出基于JSON的本地SQL查询:
1 2 3 4 5
| List<String> participants = entityManager.createNativeQuery( "SELECT p.ticket -> \"$.registrationCode\" " + "FROM participant p " + "WHERE JSON_EXTRACT(p.ticket, \"$.price\") > 1 ") .getResultList();
|
Object(s)可以修改JSON :
1 2
| event.getLocation().setCity("Constanța"); entityManager.flush();
|
Hibernate生成正确的UPDATE语句:
1 2 3
| UPDATE event SET location = '{"country":"Romania","city":"Constanța"}' WHERE id = 1
|
PostgreSQL
从9.2版开始,PostgreSQL就一直支持JSON类型。有两种类型可以使用:
- json
- jsonb
两种PostgreSQL JSON类型都需要使用二进制数据格式来实现,因此我们需要使用JsonBinaryType这次。
PostgreSQL JSON列类型
对于JSON列类型,Object(s)必须如下更改两个JSON映射:
1 2 3 4 5 6 7
| @Type(type = "jsonb") @Column(columnDefinition = "json") private Location location; @Type(type = "jsonb") @Column(columnDefinition = "json") private Ticket ticket;
|
插入和实体更新都一样,甚至可以JSON按以下方式查询该列:
1 2 3 4 5
| List<String> participants = entityManager.createNativeQuery( "SELECT p.ticket ->>'registrationCode' " + "FROM participant p " + "WHERE p.ticket ->> 'price' > '10'") .getResultList();
|
PostgreSQL JSONB列类型
对于JSONB列类型,我们只需要更改columnDefinition属性,因为json和jsonbPostgreSQL列类型都由来处理JsonBinaryType:
1 2 3 4 5 6 7
| @Type(type = "jsonb") @Column(columnDefinition = "jsonb") private Location location; @Type(type = "jsonb") @Column(columnDefinition = "jsonb") private Ticket ticket;
|
插入和JSONObject更新的工作方式相同,而JSONB列类型提供了更高级的查询功能:
1 2 3 4 5
| List<String> participants = entityManager.createNativeQuery( "SELECT jsonb_pretty(p.ticket) " + "FROM participant p " + "WHERE p.ticket ->> 'price' > '10'") .getResultList();
|
参考
How to map JSON objects using generic Hibernate Types