1. Overview
1.概述
PostgreSQL supports arrays of any type (built-in or user-defined) to be defined as types of columns of a table. In this tutorial, we’ll explore a few ways to map the PostgreSQL array with Hibernate.
PostgreSQL支持将任何类型的数组(内置或用户定义的)定义为表的列的类型。在本教程中,我们将探索一些方法,以用Hibernate映射PostgreSQL数组。
2. Basic Setup
2.基本设置
As a pre-requisite to connect with a PostgreSQL database, we should add the latest postgresql Maven dependency to our pom.xml along with the Hibernate configurations. Also, let’s create an entity class called User with the String array roles:
作为连接PostgreSQL数据库的前提条件,我们应该将最新的postgresqlMaven依赖项与Hibernate配置一起添加到我们的pom.xml。另外,让我们创建一个名为User的实体类,并使用String数组roles。
@Entity
public class User {
@Id
private Long id;
private String name;
private String[] roles;
//getters and setters
}
3. Custom Hibernate Types
3.自定义Hibernate类型
Hibernate supports custom types to map a user-defined type into SQL queries. Therefore, we can create custom types to map a PostgreSQL array with Hibernate for storing/fetching data. First, let’s create the CustomStringArrayType class implementing Hibernate’s UserType class to provide a custom type to map the String array:
Hibernate支持自定义类型,将用户定义的类型映射到SQL查询中。因此,我们可以创建自定义类型来将PostgreSQL数组与Hibernate进行映射,以存储/获取数据。首先,让我们创建CustomStringArrayType类实现Hibernate的UserType类以提供一个自定义类型来映射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
}
Here, we should note that the return type of the returnedClass method is the String array. Also, the nullSafeSet method creates an array of PostgreSQL type text.
在这里,我们应该注意,returnedClass方法的返回类型是String数组。另外,nullSafeSet方法创建了一个PostgreSQL类型text的数组。
4. Mapping Array With Custom Hibernate Types
4.用自定义Hibernate类型映射数组
4.1. User Entity
4.1.用户实体
Then, we’ll use the CustomStringArrayType class to map the String array roles to the PostgreSQL text array:
然后,我们将使用CustomStringArrayType类来将String数组roles映射到PostgreSQL的text数组。
@Entity
public class User {
//...
@Column(columnDefinition = "text[]")
@Type(type = "com.baeldung.hibernate.arraymapping.CustomStringArrayType")
private String[] roles;
//getters and setters
}
That’s it! We’re ready with our custom type implementation and array mapping to perform CRUD operations on the User entity.
这就是了!我们已经准备好使用我们的自定义类型实现和数组映射来对User实体执行CRUD操作。
4.2. Unit Test
4.2.单位测试
To test our custom type, let’s first insert a User object along with the String array roles:
为了测试我们的自定义类型,让我们首先插入一个User对象,以及String数组roles。
@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());
}
Also, we can fetch the User record that contains roles in the form of the PostgreSQL text array:
另外,我们可以获取包含roles的User记录,其形式为PostgreSQL的text数组。
@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
4.3.CustomIntegerArrayType
Similarly, we can create a custom type for various array types supported by PostgreSQL. For instance, let’s create the CustomIntegerArrayType to map the PostgreSQL int array:
同样地,我们可以为PostgreSQL支持的各种数组类型创建一个自定义类型。例如,让我们创建CustomIntegerArrayType来映射PostgreSQL的int阵列。
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
}
Similar to what we noticed in the CustomStringArrayType class, the return type of the returnedClass method is the Integer array. Also, the implementation of the nullSafeSet method creates an array of PostgreSQL type int. Last, we can use the CustomIntegerArrayType class to map Integer array locations to the PostgreSQL int array:
与我们在CustomStringArrayType类中注意到的类似,returnedClass方法的返回类型是Integer数组。另外,nullSafeSet方法的实现创建了一个PostgreSQL类型为int的数组。最后,我们可以使用CustomIntegerArrayType类来映射Integer数组位置到PostgreSQL的int数组。
@Entity
public class User {
//...
@Column(columnDefinition = "int[]")
@Type(type = "com.baeldung.hibernate.arraymapping.CustomIntegerArrayType")
private Integer[] locations;
//getters and setters
}
5. Mapping Array With hibernate-types
5.用hibernate-types映射阵列
On the other hand, instead of implementing a custom type for each type like String, Integer, and Long, we can use the hibernate-types library developed by a renowned Hibernate expert, Vlad Mihalcea.
另一方面,我们可以使用hibernate-types库,而不是为每个类型(如String、Integer和Long)实现一个自定义类型。
5.1. Setup
5.1.设置
First, we’ll add the latest hibernate-types-52 Maven dependency to our pom.xml:
首先,我们要把最新的hibernate-types-52Maven依赖性添加到我们的pom.xml。
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.10.4</version>
</dependency>
5.2. User Entity
5.2.用户实体
Next, we’ll add the integration code in the User entity to map the String array phoneNumbers:
接下来,我们将在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
}
Here, similar to the custom type CustomStringArrayType, we’ve used the StringArrayType class, provided by the hibernate-types library, as a mapper for the String array. Similarly, we can find a few other handy mappers like DateArrayType, EnumArrayType, and DoubleArrayType in the library.
在这里,与自定义类型CustomStringArrayType类似,我们使用了StringArrayType类,由hibernate-types库提供,作为String阵列的映射器。同样,我们可以在库中找到其他一些方便的映射器,如DateArrayType、EnumArrayType和DoubleArrayType等。
5.3. Unit Test
5.3.单位测试
That’s it! We’re ready with the array mapping using the hibernate-types library. Let’s update the already discussed unit test to verify the insert operation:
就是这样!我们已经准备好使用hibernate-types库进行数组映射。我们已经准备好使用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();
}
Similarly, we can verify the read operation:
同样地,我们可以验证读操作。
@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. Conclusion
6.结论
In this article, we explored mapping the PostgreSQL array with Hibernate. First, we created a custom type to map the String array using Hibernate’s UserType class. Then, we used the custom type to map the PostgreSQL text array with Hibernate. Last, we used the hibernate-types library to map the PostgreSQL array. As usual, the source code is available over on GitHub.
在这篇文章中,我们探讨了用Hibernate映射PostgreSQL数组的问题。首先,我们使用Hibernate的UserType类创建了一个自定义类型来映射String阵列。然后,我们使用自定义类型来映射PostgreSQL的text数组与Hibernate。最后,我们使用hibernate-types库来映射PostgreSQL数组。像往常一样,源代码可在GitHub上获得。