Margrop
Articles158
Tags353
Categories21
1password AC AP API AppDaemon Aqara Cron Date Diagrams.net HA HADashboard HomeAssistant IP IPv4 Java LVM‑Thin Linux MacOS MySQL NAS PPPoE PostgreSQL ProcessOn Proxmox VE SSL Shell TTS TimeMachine UML Uptime Kuma Web Windows activate ad adb adblock agent aligenie aliyun alpine annotation aop authy autofs backup baidupan bash bitwarden boot brew browser caddy2 cdn centos cert certbot charles chat chrome classloader client clone closures cloudflare cmd command commit container crontab ctyun ddsm demo dependency deploy developer devtools dll dns docker domain download draw drawio dsm dump dylib edge exception export fail2ban feign firewall-cmd flow frp frpc frps fuckgfw function gcc gfw git github golang gperftools gridea grub gvt-g hacs havcs heap hello hexo hibernate hidpi hoisting homeassistant hosts html htmlparser https idea image img img2kvm import index install intel io ios ip iptables iptv ipv6 iso java javascript jetbrains jni jnilib jpa js json jsonb jupter jupyterlab jvm k8s kernel key kid kms kodi koolproxy koolproxyr kvm lan lastpass launchctl learning lede letsencrypt linux live low-code lvm lxc m3u8 mac macos mariadb markdown maven md5 microcode mirror modem modules monitor mount mstsc mysql n2n n5105 nas network nfs node node-red nodejs nohup notepad++ npm nssm ntp oop openfeign openssl os otp ovz packet capture pat pdf pem perf ping pip plugin png powerbutton print pro proxy pve pvekclean python qcow2 qemu qemu-guest-agent rar reboot reflog remote remote desktop renew repo resize retina root route router rule rules runtime safari sata scipy-notebook scoping scp server slmgr so socks source spk spring springboot springfox ssh ssl stash string supernode svg svn swagger sync synology systemctl tap tap-windows tapwindows telecom template terminal tls token totp tvbox txt ubuntu udisk ui undertow uninstall unlocker upgrade url v2ray vhd vim vlmcsd vm vmdk web websocket wechat windows with worker wow xiaoya xml yum zip 中国电信 云电脑 交换机 光猫 公网IP 内存 内网IP 升级 启动 夏令时 天猫精灵 天翼云 安装 容器 导入 小米 常用软件 广告屏蔽 序列号 应用市场 异常 抓包 描述文件 时区 显卡虚拟化 智能家居 智能音箱 梯子 模块 流程 流程图 浏览器 漫游 激活 火绒 电信 画图 直播源 续期 网关 网络风暴 群晖 腾讯 虚拟机 证书 路由 路由器 软件管家 软路由 运维监控 镜像 镜像源 门窗传感器 防火墙 阿里云 阿里源 集客

Hitokoto

Archive

如何使用通用的Hibernate类型映射JSON对象

如何使用通用的Hibernate类型映射JSON对象

介绍

在本文中,我们将看到如何使用Hibernate Types开源项目将JSON列映射到JPA实体属性。

虽然您可以创建自己的自定义Hibernate类型,但可以在Oracle,SQL Server,PostgreSQL或MySQL上映射JSON列类型,但是由于Hibernate Types项目已经提供了此功能,因此您无需实现自己的Hibernate Type 。

领域模型

假设我们具有以下域模型:
JsonType域模型

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 {
//Code omitted for brevity
}

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

Author:Margrop
Link:http://blog.margrop.com/post/how-to-map-json-collections-using-jpa-and-hibernate/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可