1. 概述

PostgreSQL 支持将任何类型(内置或用户定义)的数组定义为表的列类型 。在本教程中,我们将探索使用Hibernate映射 PostgreSQL 数组的几种方法。

2. 基本设置

作为连接 PostgreSQL 数据库的先决条件,我们应该将最新的 postgresql Maven 依赖项与 Hibernate 配置一起添加到 pom.xml 中。另外,让我们使用 字符串 数组 Roles 创建一个名为 User 的实体类

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

    private String[] roles;

    //getters and setters 
}

3. 自定义 Hibernate 类型

Hibernate 支持自定义类型以将用户定义的类型映射到 SQL 查询。因此, 我们可以创建自定义类型来将 PostgreSQL 数组映射到 Hibernate 来存储/获取数据。首先,我们创建 CustomStringArrayType来实现 Hibernate 的 UserType,以提供自定义类型来映射 String 数组:

public class CustomStringArrayType implements UserType<String[]> {
    @Override
    public int sqlType() {
        return Types.ARRAY;
    }

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

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

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

这里需要注意的是 returnedClass 方法的返回类型是 String 数组 。此外, nullSafeSet 方法创建一个 PostgreSQL 类型 text 的数组

4. 使用自定义 Hibernate 类型映射数组

4.1. 用户 实体

然后,我们将使用 CustomStringArrayType 类将 字符串 数组 角色 映射到 PostgreSQL 文本 数组:

@Entity
public class User {
    //...

    @Column(columnDefinition = "text[]")
    @Type(value = com.baeldung.hibernate.arraymapping.CustomStringArrayType.class)
    private String[] roles;
  
   //getters and setters 
}

就是这样!我们已准备好使用自定义类型实现和数组映射来对 User 实体执行 CRUD 操作。

4.2.单元测试

为了测试我们的自定义类型,我们首先插入一个 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());
    assertEquals("admin", userDBObj.getRoles()[0]);
    assertEquals(578, userDBObj.getLocations()[1]);
}

此外,我们还可以以 PostgreSQL 文本 数组的形式获取包含 角色 的用户 记录:

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

    assertEquals("john", user.getName());
    assertEquals("superuser", user.getRoles()[0]);
    assertEquals("employee", user.getRoles()[1]);
    assertEquals("admin", user.getRoles()[1]);
    assertEquals(100, user.getLocations()[0]);
    assertEquals(389, user.getLocations()[1]);
    assertEquals("7000000000", user.getPhoneNumbers()[0]);
    assertEquals("8000000000", user.getPhoneNumbers()[1]);
}

4.3. 自定义整数数组类型

同样,我们可以为 PostgreSQL 支持的各种数组类型创建自定义类型。例如,让我们创建 CustomIntegerArrayType 来映射 PostgreSQL int 数组:

public class CustomIntegerArrayType implements UserType<Integer[]> {
    @Override
    public int sqlType() {
        return Types.ARRAY;
    }

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

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

    @Override
    public void nullSafeSet(PreparedStatement st, Integer[] value, int index, 
      SharedSessionContractImplementor session) throws SQLException {
        if (st != null) {
          if (value != null) {
            Array array = session.getJdbcConnectionAccess().obtainConnection().createArrayOf("int", value);
            st.setArray(index, array);
          } else {
            st.setNull(index, Types.ARRAY);
          }
        }
    }

    //implement equals, hashCode, and other methods 
}

与我们在 CustomStringArrayType 类中注意到的类似, returnedClass 方法的返回类型是 Integer array 。此外, nullSafeSet 方法的实现会创建一个 PostgreSQL 类型 int 的数组 。最后,我们可以使用 CustomIntegerArrayType 类将 Integer 数组 位置 映射到 PostgreSQL int 数组:

@Entity
public class User {
    //...
    
    @Column(columnDefinition = "int[]")
    @Type(value = com.baeldung.hibernate.arraymapping.CustomIntegerArrayType.class)
    private Integer[] locations;

    //getters and setters
}

5. 使用 hibernate-types 映射数组

另一方面,我们可以使用由著名 Hibernate 专家 Vlad Mihalcea 开发的 hibernate-types,而不是为每种类型(如 StringIntegerLong )实现自定义类型。

5.1.设置

首先,我们将最新的 hibernate-types-60 Maven 依赖项添加到 pom.xml 中:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-60</artifactId>
    <version>2.21.1</version>
</dependency>

5.2. 用户 实体

接下来,我们将在 User 实体中添加集成代码以映射 字符串 数组 phoneNumbers

@Entity
public class User {
    //...
    @Type(StringArrayType.class)
    @Column(
        name = "phone_numbers",
        columnDefinition = "text[]"
    )
    private String[] phoneNumbers;

    //getters and setters
}

在这里,与自定义类型 CustomStringArrayType 类似,我们使用 hibernate-types 库提供的 StringArrayType 类作为 String 数组的映射器。同样,我们可以 在库中找到其他一些方便的映射器,例如 DateArrayTypeEnumArrayTypeDoubleArrayType

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("john", user.getName());
    assertEquals("superuser", user.getRoles()[0]);
    assertEquals("admin", user.getRoles()[1]);
    assertEquals(100, user.getLocations()[0]);
    assertEquals(389, user.getLocations()[1]);
    assertEquals("7000000000", user.getPhoneNumbers()[0]);
    assertEquals("8000000000", user.getPhoneNumbers()[1]);
}

六,结论

在本文中,我们探讨了使用 Hibernate 映射 PostgreSQL 数组。首先,我们创建了一个自定义类型来使用 Hibernate 的 UserType 类映射 String 数组。然后,我们使用自定义类型将 PostgreSQL 文本 数组与 Hibernate 进行映射。最后,我们使用 hibernate-types 库来映射 PostgreSQL 数组。与往常一样,源代码可在 GitHub 上获取。