«1. Обзор
В этом руководстве мы рассмотрим типы Hibernate. Эта библиотека предоставляет нам несколько типов, которых нет в ядре Hibernate ORM.
2. Зависимости
Чтобы включить Hibernate Types, мы просто добавим соответствующую зависимость hibernate-types:
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.9.7</version>
</dependency>
Это будет работать с версиями Hibernate 5.4, 5.3 и 5.2.
В случае, если версия Hibernate более старая, указанное выше значение ArtiftId будет другим. Для версий 5.1 и 5.0 мы можем использовать hibernate-types-51. Точно так же для версии 4.3 требуется hibernate-types-43, а для версий 4.2 и 4.1 требуется hibernate-types-4.
Для примеров в этом руководстве требуется база данных. Используя Docker, мы предоставили контейнер базы данных. Поэтому нам понадобится рабочая копия Docker.
Итак, чтобы запустить и создать нашу базу данных, нам нужно только выполнить:
$ ./create-database.sh
3. Поддерживаемые базы данных
Мы можем использовать наши типы с базами данных Oracle, SQL Server, PostgreSQL и MySQL. Поэтому сопоставление типов в Java с типами столбцов базы данных зависит от используемой нами базы данных. В нашем случае мы будем использовать MySQL и сопоставлять JsonBinaryType с типом столбца JSON.
Документацию по поддерживаемым сопоставлениям можно найти в репозитории Hibernate Types.
4. Модель данных
Модель данных для этого урока позволит нам хранить информацию об альбомах и песнях. Альбом имеет обложку и одну или несколько песен. У песни есть исполнитель и длина. Обложка имеет два URL изображения и код UPC. Наконец, у артиста есть имя, страна и музыкальный жанр.
Раньше мы создавали таблицы для представления всех данных в нашей модели. Но теперь, когда у нас есть доступные типы, мы можем очень легко хранить некоторые данные в формате JSON.
В этом уроке мы создадим таблицы только для альбомов и песен:
public class Album extends BaseEntity {
@Type(type = "json")
@Column(columnDefinition = "json")
private CoverArt coverArt;
@OneToMany(fetch = FetchType.EAGER)
private List<Song> songs;
// other class members
}
public class Song extends BaseEntity {
private Long length = 0L;
@Type(type = "json")
@Column(columnDefinition = "json")
private Artist artist;
// other class members
}
public class Artist implements Serializable {
private String name;
private String country;
private String genre;
// other class members
}
public class CoverArt implements Serializable {
private String frontCoverArtUrl;
private String backCoverArtUrl;
private String upcCode;
// other class members
}
Используя JsonStringType, мы представим обложку и исполнителей в виде столбцов JSON в этих таблицах: ~~ ~
Важно отметить, что классы Artist и CoverArt являются POJO, а не сущностями. Кроме того, они являются членами наших классов сущностей базы данных, определенных с помощью аннотации @Type(type = «json»).
4.1. Хранение типов JSON
@TypeDefs({
@TypeDef(name = "json", typeClass = JsonStringType.class),
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
})
public class BaseEntity {
// class members
}
Мы определили наши модели альбомов и песен, чтобы они содержали членов, которые база данных будет хранить как JSON. Это связано с использованием предоставленного типа json. Чтобы этот тип был доступен для использования, мы должны определить его с помощью определения типа:
@Type для JsonStringType и JsonBinaryType делает доступными типы json и jsonb.
Последние версии MySQL поддерживают JSON в качестве типа столбца. Следовательно, JDBC обрабатывает любое чтение JSON или любой объект, сохраненный в столбце с любым из этих типов, как строку. Это означает, что для правильного отображения столбца мы должны использовать JsonStringType в нашем определении типа.
4.2. Hibernate
insert into song (name, artist, length, id) values ('A Happy Song', '{"name":"Superstar","country":"England","genre":"Pop"}', 240, 3);
insert into song (name, artist, length, id) values ('A Sad Song', '{"name":"Superstar","country":"England","genre":"Pop"}', 120, 4);
insert into song (name, artist, length, id) values ('A New Song', '{"name":"Newcomer","country":"Jamaica","genre":"Reggae"}', 300, 6)
insert into album (name, cover_art, id) values ('Album 0', '{"frontCoverArtUrl":"http://fakeurl-0","backCoverArtUrl":"http://fakeurl-1","upcCode":"b2b9b193-ee04-4cdc-be8f-3a276769ab5b"}', 7)
В конечном счете, наши типы будут автоматически транслироваться в SQL с использованием JDBC и Hibernate. Итак, теперь мы можем создать несколько объектов песни, объект альбома и сохранить их в базе данных. Затем Hibernate генерирует следующие операторы SQL:
Как и ожидалось, все наши Java-объекты типа json переводятся Hibernate и сохраняются в нашей базе данных в формате JSON правильного формата.
5. Хранение универсальных типов
Помимо поддержки столбцов на основе JSON, библиотека также добавляет несколько универсальных типов: YearMonth, Year и Month из пакета java.time.
Теперь мы можем отображать эти типы, которые изначально не поддерживаются Hibernate или JPA. Кроме того, теперь у нас есть возможность хранить их в виде столбца Integer, String или Date.
@TypeDef(
typeClass = YearMonthIntegerType.class,
defaultForType = YearMonth.class
)
public class Song extends BaseEntity {
@Column(
name = "recorded_on",
columnDefinition = "mediumint"
)
private YearMonth recordedOn = YearMonth.now();
// other class members
}
Например, предположим, что мы хотим добавить дату записи песни в нашу модель песни и сохранить ее как целое число в нашей базе данных. Мы можем использовать YearMonthIntegerType в определении нашего класса сущностей Song:
Наше значение свойства RecordOn транслируется в предоставленный нами typeClass. В результате предопределенный преобразователь сохранит значение в нашей базе данных как целое число.
6. Другие служебные классы
«В Hibernate Types есть несколько вспомогательных классов, которые еще больше улучшают работу разработчиков при использовании Hibernate.
CamelCaseToSnakeCaseNamingStrategy сопоставляет свойства в стиле верблюда в наших классах Java со столбцами в формате змеи в нашей базе данных.
ClassImportIntegrator допускает простые значения имени класса Java DTO в параметрах конструктора JPA.
Существуют также классы ListResultTransformer и MapResultTransformer, предоставляющие более четкие реализации объектов результатов, используемых JPA. Кроме того, они поддерживают использование лямбда-выражений и обеспечивают обратную совместимость со старыми версиями JPA.
7. Заключение
В этом руководстве мы представили библиотеку Java Hibernate Types и новые типы, которые она добавляет в Hibernate и JPA. Мы также рассмотрели некоторые утилиты и универсальные типы, предоставляемые библиотекой.