Перейти к основному содержимому

Сопоставление массива PostgreSQL с Hibernate

· 5 мин. чтения

1. Обзор

PostgreSQL поддерживает определение массивов любого типа (встроенных или определяемых пользователем) как типов столбцов таблицы . В этом руководстве мы рассмотрим несколько способов сопоставления массива PostgreSQL с помощью Hibernate .

2. Базовая установка

В качестве предварительного условия для подключения к базе данных PostgreSQL мы должны добавить последнюю зависимость postgresql Maven в наш pom.xml вместе с конфигурациями Hibernate. Кроме того, давайте создадим класс сущности с именем User с ролями массива String : ``

@Entity
public class User {
@Id
private Long id;
private String name;

private String[] roles;

//getters and setters
}

3. Пользовательские типы гибернации

Hibernate поддерживает пользовательские типы для сопоставления пользовательского типа с запросами SQL. Поэтому мы можем создавать собственные типы для сопоставления массива PostgreSQL с Hibernate для хранения/выборки данных. Во-первых, давайте создадим класс CustomStringArrayType , реализующий класс UserType Hibernate, `` чтобы предоставить пользовательский тип для сопоставления массива String :

public class CustomStringArrayType implements UserType {
@Override
public int[] sqlTypes() {
return new int[]{Types.ARRAY};
}

@Override
public Class returnedClass() {
return String[].class;
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
Array array = rs.getArray(names[0]);
return array != null ? array.getArray() : null;
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (value != null && st != null) {
Array array = session.connection().createArrayOf("text", (String[])value);
st.setArray(index, array);
} else {
st.setNull(index, sqlTypes()[0]);
}
}
//implement equals, hashCode, and other methods
}

Здесь следует отметить, что возвращаемый тип метода returnClass — это массив String . Также метод nullSafeSet создает массив PostgreSQL типа text .

4. Сопоставление массива с пользовательскими типами гибернации

4.1. Пользовательская сущность

Затем мы будем использовать класс CustomStringArrayType для сопоставления ролей массива String с текстовым массивом PostgreSQL :

@Entity
public class User {
//...

@Column(columnDefinition = "text[]")
@Type(type = "com.foreach.hibernate.arraymapping.CustomStringArrayType")
private String[] roles;

//getters and setters
}

Вот и все! Мы готовы с нашей собственной реализацией типа и сопоставлением массива для выполнения операций CRUD над сущностью пользователя .

4.2. Модульный тест

Чтобы протестировать наш пользовательский тип, давайте сначала вставим объект User вместе с ролями массива String : ``

@Test
public void givenArrayMapping_whenArraysAreInserted_thenPersistInDB()
throws HibernateException, IOException {
transaction = session.beginTransaction();

User user = new User();
user.setId(2L);
user.setName("smith");

String[] roles = {"admin", "employee"};
user.setRoles(roles);

session.persist(user);
session.flush();
session.clear();

transaction.commit();

User userDBObj = session.find(User.class, 2L);

assertEquals("smith", userDBObj.getName());
}

Также мы можем получить запись пользователя , содержащую роли , в виде текстового массива PostgreSQL :

@Test
public void givenArrayMapping_whenQueried_thenReturnArraysFromDB()
throws HibernateException, IOException {
User user = session.find(User.class, 2L);

assertEquals("smith", user.getName());
assertEquals("admin", user.getRoles()[0]);
assertEquals("employee", user.getRoles()[1]);
}

4.3. CustomIntegerArrayType

Точно так же мы можем создать собственный тип для различных типов массивов, поддерживаемых PostgreSQL. Например, давайте создадим CustomIntegerArrayType для сопоставления массива int PostgreSQL :

public class CustomIntegerArrayType implements UserType {
@Override
public int[] sqlTypes() {
return new int[]{Types.ARRAY};
}

@Override
public Class returnedClass() {
return Integer[].class;
}

@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner)
throws HibernateException, SQLException {
Array array = rs.getArray(names[0]);
return array != null ? array.getArray() : null;
}

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session)
throws HibernateException, SQLException {
if (value != null && st != null) {
Array array = session.connection().createArrayOf("int", (Integer[])value);
st.setArray(index, array);
} else {
st.setNull(index, sqlTypes()[0]);
}
}

//implement equals, hashCode, and other methods
}

Подобно тому, что мы заметили в классе CustomStringArrayType , возвращаемый тип метода returnClass — массив Integer . Также реализация метода nullSafeSet создает массив PostgreSQL типа int . Наконец, мы можем использовать класс CustomIntegerArrayType для сопоставления местоположений массива Integer с массивом int PostgreSQL :

@Entity
public class User {
//...

@Column(columnDefinition = "int[]")
@Type(type = "com.foreach.hibernate.arraymapping.CustomIntegerArrayType")
private Integer[] locations;

//getters and setters
}

5. Сопоставление массива с спящими типами

С другой стороны, вместо реализации собственного типа для каждого типа, такого как String , Integer и Long , мы можем использовать библиотеку типов hibernate, разработанную известным экспертом по Hibernate Владом Михалча.

5.1. Настраивать

Во- первых, мы добавим последнюю зависимость Maven hibernate-types-52 в наш pom.xml :

<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.10.4</version>
</dependency>

5.2. Пользовательская сущность

Далее мы добавим код интеграции в сущность User для сопоставления массива String phoneNumbers :

@TypeDefs({
@TypeDef(
name = "string-array",
typeClass = StringArrayType.class
)
})
@Entity
public class User {
//...
@Type(type = "string-array")
@Column(
name = "phone_numbers",
columnDefinition = "text[]"
)
private String[] phoneNumbers;

//getters and setters
}

Здесь, подобно пользовательскому типу CustomStringArrayType , мы использовали класс StringArrayType , предоставленный библиотекой hibernate-types , в качестве преобразователя для массива String . Точно так же мы можем найти в библиотеке несколько других удобных преобразователей, таких как DateArrayType , EnumArrayType и DoubleArrayType .

5.3. Модульный тест

Вот и все! Мы готовы к отображению массива с помощью библиотеки hibernate-types . Обновим уже обсуждавшийся модульный тест, чтобы проверить операцию вставки:

@Test
public void givenArrayMapping_whenArraysAreInserted_thenPersistInDB()
throws HibernateException, IOException {
transaction = session.beginTransaction();

User user = new User();
user.setId(2L);
user.setName("smith");

String[] roles = {"admin", "employee"};
user.setRoles(roles);

String[] phoneNumbers = {"7000000000", "8000000000"};
user.setPhoneNumbers(phoneNumbers);

session.persist(user);
session.flush();
session.clear();

transaction.commit();
}

Точно так же мы можем проверить операцию чтения:

@Test
public void givenArrayMapping_whenQueried_thenReturnArraysFromDB()
throws HibernateException, IOException {
User user = session.find(User.class, 2L);

assertEquals("smith", user.getName());
assertEquals("admin", user.getRoles()[0]);
assertEquals("employee", user.getRoles()[1]);
assertEquals("7000000000", user.getPhoneNumbers()[0]);
assertEquals("8000000000", user.getPhoneNumbers()[1]);
}

6. Заключение

В этой статье мы рассмотрели сопоставление массива PostgreSQL с Hibernate. Во-первых, мы создали собственный тип для отображения массива String с помощью класса UserType Hibernate. Затем мы использовали пользовательский тип для сопоставления текстового массива PostgreSQL с Hibernate. Наконец, мы использовали библиотеку hibernate-types для сопоставления массива PostgreSQL. Как обычно, исходный код доступен на GitHub .