«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. Мы также рассмотрели некоторые утилиты и универсальные типы, предоставляемые библиотекой.