Вступление
Проект hibernate-types
открытым исходным кодом позволяет отображать объекты Java или JsonNode
Джексона как свойства сущности JPA.
Недавно, благодаря нашим замечательным авторам, мы добавили поддержку сохраняющих тип коллекций, которые будут сохраняться как JSON. В этой статье вы узнаете, как достичь этой цели.
Maven зависимость
Прежде всего, вам нужно настроить следующую зависимость Maven в pom.xml
конфигурации вашего проекта pom.xml
:
1
2
3
4
5
|
< dependency > < groupId >com.vladmihalcea</ groupId > < artifactId >hibernate-types-52</ artifactId > < version >${hibernate-types.version}</ version > </ dependency > |
Если вы используете более старые версии Hibernate, обратитесь к репозиторию GitHub hibernate-types
для получения дополнительной информации о зависимости соответствия для текущей версии Hibernate.
Модель предметной области
Предположим, у нас есть следующий тип объекта Location
Java.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class Location implements Serializable { private String country; private String city; //Getters and setters omitted for brevity @Override public String toString() { return "Location{" + "country='" + country + '\ '' + ", city='" + city + '\ '' + '}' ; } } |
И одна сущность Event
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Entity (name = "Event" ) @Table (name = "event" ) public class Event extends BaseEntity { @Type (type = "jsonb" ) @Column (columnDefinition = "jsonb" ) private Location location; @Type ( type = "jsonb" , parameters = { @org .hibernate.annotations.Parameter( name = TypeReferenceFactory.FACTORY_CLASS, value = "com.vladmihalcea.hibernate.type.json.PostgreSQLGenericJsonBinaryTypeTest$AlternativeLocationsTypeReference" ) } ) @Column (columnDefinition = "jsonb" ) private List<Location> alternativeLocations = new ArrayList<Location>(); //Getters and setters omitted for brevity } |
BaseEntity
определяет некоторые базовые свойства (например, @Id
, @Version
) и несколько таможенных типов Hibernate, среди которых нас интересует JsonBinaryType
.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
@TypeDefs ({ @TypeDef (name = "string-array" , typeClass = StringArrayType. class ), @TypeDef (name = "int-array" , typeClass = IntArrayType. class ), @TypeDef (name = "json" , typeClass = JsonStringType. class ), @TypeDef (name = "jsonb" , typeClass = JsonBinaryType. class ), @TypeDef (name = "jsonb-node" , typeClass = JsonNodeBinaryType. class ), @TypeDef (name = "json-node" , typeClass = JsonNodeStringType. class ), }) @MappedSuperclass public class BaseEntity { @Id private Long id; @Version private Integer version; //Getters and setters omitted for brevity } |
Чтобы узнать больше об использовании @MappedSuperclass
, ознакомьтесь с этой статьей .
TypeReferenceFactory
Чтобы сохранить объект Location
в jsonb
PostgreSQL, нам просто нужно аннотировать свойство location
с помощью @Type(type = "jsonb")
.
Однако для коллекции alternativeLocations
нам необходимо предоставить связанный TypeReference
Jackson TypeReference
чтобы мы могли восстановить ту же самую безопасную для типов коллекцию Java при чтении объекта JSON из реляционной базы данных.
Для этой цели мы предоставляем полностью определенный класс реализации TypeReferenceFactory
который выглядит следующим образом:
1
2
3
4
5
6
7
8
|
public static class AlternativeLocationsTypeReference implements TypeReferenceFactory { @Override public TypeReference<?> newTypeReference() { return new TypeReference<List<Location>>() {}; } } |
Это оно!
Время тестирования
При сохранении следующего объекта Event
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
Location cluj = new Location(); cluj.setCountry( "Romania" ); cluj.setCity( "Cluj-Napoca" ); Location newYork = new Location(); newYork.setCountry( "US" ); newYork.setCity( "New-York" ); Location london = new Location(); london.setCountry( "UK" ); london.setCity( "London" ); Event event = new Event(); event.setId(1L); event.setLocation(cluj); event.setAlternativeLocations( Arrays.asList(newYork, london) ); entityManager.persist(event); |
Hibernate сгенерирует следующий оператор SQL INSERT:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
INSERT INTO event ( version, alternativeLocations, location, id ) VALUES ( 0, [ { "country" : "US" , "city" : "New-York" }, { "country" : "UK" , "city" : "London" } ], { "country" : "Romania" , "city" : "Cluj-Napoca" }, 1 ) |
Кроме того, при получении обратно объекта Event
, свойства location
и alternativeLocations правильно выбираются:
Событие события = entityManager.find (Event.class, eventId);
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
assertEquals( "Cluj-Napoca" , event.getLocation().getCity() ); assertEquals( 2 , event.getAlternativeLocations().size()); assertEquals( "New-York" , event.getAlternativeLocations().get( 0 ).getCity() ); assertEquals( "London" , event.getAlternativeLocations().get( 1 ).getCity() ); |
Круто, верно?
Смотрите оригинальную статью здесь: Как отобразить коллекции JSON с использованием JPA и Hibernate
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |