Автор оригинала: Vlad Mihalcea.
Вступление
В этой статье мы рассмотрим, как сопоставить тип столбца интервала PostgreSQL с объектом длительности Java с помощью Hibernate и hibernate-типов проекта.
Еще одна очень полезная функция, представленная проектом hibernate-types , заключается в том, что все типы, расширяющие Неизменяемый тип , теперь могут рассматриваться как стандартные org.hibernate.type.Введите , что позволит значительно улучшить интеграцию API ядра Hibernate.
Модель предметной области
Предполагая, что у нас есть следующая таблица книга базы данных, которая определяет столбец presale_period типа интервал|/.
Мы можем сопоставить таблицу book с сущностью Book JPA следующим образом:
@Entity(name = "Book")
@Table(name = "book")
@TypeDef(
typeClass = PostgreSQLIntervalType.class,
defaultForType = Duration.class
)
@TypeDef(
typeClass = YearMonthDateType.class,
defaultForType = YearMonth.class
)
public class Book {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String isbn;
private String title;
@Column(
name = "published_on",
columnDefinition = "date"
)
private YearMonth publishedOn;
@Column(
name = "presale_period",
columnDefinition = "interval"
)
private Duration presalePeriod;
public Long getId() {
return id;
}
public Book setId(Long id) {
this.id = id;
return this;
}
public String getIsbn() {
return isbn;
}
public Book setIsbn(String isbn) {
this.isbn = isbn;
return this;
}
public String getTitle() {
return title;
}
public Book setTitle(String title) {
this.title = title;
return this;
}
public YearMonth getPublishedOn() {
return publishedOn;
}
public Book setPublishedOn(YearMonth publishedOn) {
this.publishedOn = publishedOn;
return this;
}
public Duration getPresalePeriod() {
return presalePeriod;
}
public Book setPresalePeriod(Duration presalePeriod) {
this.presalePeriod = presalePeriod;
return this;
}
}
Первое, что следует заметить, – это то, что методы настройки свойств сущности следуют шаблону проектирования интерфейса Fluent .
Второе, что вы можете заметить, это то, что мы определяем бизнес-ключ @NaturalID, который позволяет нам извлекать сущность на основе естественного идентификатора, даже если мы не знаем значения первичного ключа связанной записи таблицы.
Третье, что вы заметите, это то, что мы определяем несколько @TypeDef аннотаций. Оба предназначены для типов объектов, представленных Java 8.
Чтобы отобразить тип Java Год Месяц , мы можем использовать Тип даты Год Месяц , как описано в этой статье .
Чтобы сопоставить столбец интервала PostgreSQL с Java Длительность , нам нужно использовать тип интервала PostgreSQL , предлагаемый проектом hibernate-types .
Сопоставление столбцов интервала Java с интервалом PostgreSQL
При сохранении Книги сущности:
entityManager.persist(
new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setPublishedOn(YearMonth.of(2016, 10))
.setPresalePeriod(
Duration.between(
LocalDate
.of(2015, Month.NOVEMBER, 2)
.atStartOfDay(),
LocalDate
.of(2016, Month.AUGUST, 25)
.atStartOfDay()
)
)
);
Мы видим, что Hibernate генерирует соответствующую инструкцию SQL INSERT:
INSERT INTO book (
isbn,
presale_period,
published_on,
title,
id
)
VALUES (
'978-9730228236',
'0 years 0 mons 297 days 0 hours 0 mins 0.00 secs',
'2016-10-01',
'High-Performance Java Persistence',
1
)
При извлечении сущности Book мы видим, что атрибут Период до продажи Java Продолжительность правильно заполнен соответствующим значением столбца PostgreSQL интервал|/.
Book book = entityManager
.unwrap(Session.class)
.bySimpleNaturalId(Book.class)
.load("978-9730228236");
assertEquals(
Duration.between(
LocalDate
.of(2015, Month.NOVEMBER, 2)
.atStartOfDay(),
LocalDate
.of(2016, Month.AUGUST, 25)
.atStartOfDay()
),
book.getPresalePeriod()
);
Хотя сохранение и извлечение сущности Book , а также выполнение любого запроса JPQL и API критериев довольно просто, обработка собственных наборов результатов SQL-запросов сложнее при работе с типами столбцов, которые изначально не поддерживаются Hibernate.
Если Hibernate обнаруживает типы столбцов JDBC, для которых у него нет зарегистрированного типа Hibernate, то возникает исключение Без сопоставления диалектов для типа JDBC .
Как я объяснил в этой статье , вы можете решить эту проблему, указав правильный тип гибернации для обработки данного типа столбца JDBC.
В следующем примере собственного SQL-запроса вы можете видеть, что псевдоним столбца published_on результирующего набора настроен на использование типа YearMonthDateType , в то время как псевдоним столбца presale_period обрабатывается типом PostgreSQLIntervalType .
Tuple result = (Tuple) entityManager
.createNativeQuery(
"SELECT " +
" b.published_on AS published_on, " +
" b.presale_period AS presale_period " +
"FROM " +
" book b " +
"WHERE " +
" b.isbn = :isbn ", Tuple.class)
.setParameter("isbn", "978-9730228236")
.unwrap(NativeQuery.class)
.addScalar(
"published_on",
YearMonthDateType.INSTANCE
)
.addScalar(
"presale_period",
PostgreSQLIntervalType.INSTANCE
)
.getSingleResult();
assertEquals(
YearMonth.of(2016, 10),
result.get("published_on")
);
assertEquals(
Duration.between(
LocalDate.of(2015, Month.NOVEMBER, 2).atStartOfDay(),
LocalDate.of(2016, Month.AUGUST, 25).atStartOfDay()
),
result.get("presale_period")
);
Метод addScalar интерфейса Hibernate Собственный запрос принимает org.hibernate.тип.Введите Ссылку на объект, в то время как YearMonthDateType и PostgreSQLIntervalType реализуют интерфейс UserType|/.
До выпуска 2.6 типов гибернации было невозможно использовать Неизменяемый тип , который расширяет интерфейс UserType , в вызовах методов addScalar . Однако начиная с версии 2.6 класс ImmutableType abstract реализует как Тип пользователя , так и org.hibernate.type.Введите , поэтому передача неизменяемого типа (который является базовым классом как YearMonthDateType , так и PostgreSQLIntervalType ) в метод addScalar больше не является проблемой.
Вывод
Проект hibernate-types расширился, чтобы вместить большое разнообразие типов гибернации, которые изначально не поддерживаются. Например, теперь вы можете использовать JSON, МАССИВ, HStore, Диапазон, Int, Месяц года, Символ, допускающий значение null, и типы перечислений, специфичные для PostgreSQL.
Хотя вы также можете реализовать все эти типы самостоятельно, гораздо удобнее определить типы гибернации зависимости в вашем проекте pom.xml Файл конфигурации Maven и сосредоточьтесь на бизнес-логике приложения вместо написания типов, специфичных для гибернации.