1. 概要

PostgreSQLは、テーブルの列のタイプとして定義される任意のタイプ(組み込みまたはユーザー定義)の配列をサポートします。 このチュートリアルでは、PostgreSQLアレイをHibernateでマッピングするいくつかの方法を探ります。

2. 基本設定

PostgreSQLデータベースに接続するための前提条件として、最新の postgresqlMaven依存関係をHibernate構成とともにpom.xmlに追加する必要があります。 また、String配列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 {
    @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 
}

ここで、returnedClassメソッドのreturnタイプはString配列であることに注意してください。 また、 nullSafeSetメソッドは、PostgreSQLタイプのtextの配列を作成します。

4. カスタムHibernateタイプを使用した配列のマッピング

4.1. ユーザーエンティティ

次に、 CustomStringArrayType クラスを使用して、String配列rolesをPostgreSQLtext配列にマップします。

@Entity
public class User {
    //...

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

それでおしまい! カスタムタイプの実装と配列マッピングを使用して、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());
}

また、PostgreSQL text配列の形式でrolesを含むUserレコードをフェッチできます。

@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 を作成して、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 
}

CustomStringArrayType クラスで気付いたのと同様に、returnedClassメソッドの戻り型は整数配列です。 また、 nullSafeSetメソッドの実装により、PostgreSQLタイプintの配列が作成されます。 最後に、 CustomIntegerArrayType クラスを使用して、Integer配列の場所をPostgreSQLint配列にマップできます。

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

    //getters and setters
}

5. hibernate-typesを使用した配列のマッピング

一方、 String Integer Long などのタイプごとにカスタムタイプを実装する代わりに、hibernateタイプを使用できます。ライブラリは、Hibernateの有名な専門家であるVladMihalceaによって開発されました。

5.1. 設定

まず、最新の hibernate-types-52Maven依存関係を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 と同様に、hibernate-typesライブラリによって提供されるStringArrayTypeクラスを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でマッピングする方法について説明しました。 最初に、HibernateのUserTypeクラスを使用してString配列をマップするカスタムタイプを作成しました。 次に、カスタムタイプを使用して、PostgreSQL text配列をHibernateにマップしました。 最後に、hibernate-typesライブラリを使用してPostgreSQL配列をマップしました。 いつものように、ソースコードはGitHubから入手できます。