1. 概要

ほとんどのJPA駆動型アプリケーションは、HibernateOpenJPAなどのJPA実装を取得するために「persistence.xml」ファイルを多用します。

ここでのアプローチは、1つ以上の永続性ユニットおよび関連する永続性コンテキストを構成するための集中型メカニズムを提供します。

このアプローチは本質的に間違っているわけではありませんが、異なる永続性ユニットを使用するアプリケーションコンポーネントを個別にテストする必要があるユースケースには適していません。

明るい面として、プレーンなJava を使用するだけで、「persistence.xml」ファイルにまったく頼らずにJPA実装をブートストラップすることができます。

このチュートリアルでは、Hibernateを使用してこれを実現する方法を説明します。

2. PersistenceUnitInfoインターフェースの実装

典型的な「xmlベース」のJPA構成では、JPA実装がPersistenceUnitInfoインターフェースの実装を自動的に処理します。

「persistence.xml」ファイルの解析によって収集されたすべてのデータを使用して、永続性プロバイダーはこの実装を使用してエンティティマネージャーファクトリを作成します。 この工場から、エンティティマネージャーを取得できます。

「persistence.xml」ファイルに依存しないため、最初に行う必要があるのは、独自のPersistenceUnitInfo実装を提供することです。永続プロバイダーにはHibernateを使用します。

public class HibernatePersistenceUnitInfo implements PersistenceUnitInfo {
    
    public static String JPA_VERSION = "2.1";
    private String persistenceUnitName;
    private PersistenceUnitTransactionType transactionType
      = PersistenceUnitTransactionType.RESOURCE_LOCAL;
    private List<String> managedClassNames;
    private List<String> mappingFileNames = new ArrayList<>();
    private Properties properties;
    private DataSource jtaDataSource;
    private DataSource nonjtaDataSource;
    private List<ClassTransformer> transformers = new ArrayList<>();
    
    public HibernatePersistenceUnitInfo(
      String persistenceUnitName, List<String> managedClassNames, Properties properties) {
        this.persistenceUnitName = persistenceUnitName;
        this.managedClassNames = managedClassNames;
        this.properties = properties;
    }

    // standard setters / getters   
}

簡単に言うと、 HibernatePersistenceUnitInfo クラスは、特定の永続性ユニットにバインドされた構成パラメーターを格納する単なるデータコンテナーです。 これには、永続ユニット名、管理対象エンティティクラスの名前、トランザクションタイプ、JTAおよび非JTAデータソースなどが含まれます。

3. HibernateのEntityManagerFactoryBuilderImplクラスを使用したエンティティマネージャーファクトリの作成

カスタムPersistenceUnitInfo実装が用意されたので、最後に行う必要があるのは、エンティティマネージャーファクトリを取得することです。

Hibernateは、EntityManagerFactoryBuilderImplクラス(ビルダーパターンの適切な実装)を使用して、このプロセスを簡単にします。

より高いレベルの抽象化を提供するために、EntityManagerFactoryBuilderImpl。の機能をラップするクラスを作成しましょう。

まず、HibernateのEntityManagerFactoryBuilderImplクラスとHibernatePersistenceUnitInf oクラスを使用して、エンティティマネージャーファクトリとエンティティマネージャーの作成を処理するメソッドを紹介します。

public class JpaEntityManagerFactory {
    private String DB_URL = "jdbc:mysql://databaseurl";
    private String DB_USER_NAME = "username";
    private String DB_PASSWORD = "password";
    private Class[] entityClasses;
    
    public JpaEntityManagerFactory(Class[] entityClasses) {
        this.entityClasses = entityClasses;
    }
    
    public EntityManager getEntityManager() {
        return getEntityManagerFactory().createEntityManager();
    }
    
    protected EntityManagerFactory getEntityManagerFactory() {
        PersistenceUnitInfo persistenceUnitInfo = getPersistenceUnitInfo(
          getClass().getSimpleName());
        Map<String, Object> configuration = new HashMap<>();
        return new EntityManagerFactoryBuilderImpl(
          new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration)
          .build();
    }
    
    protected HibernatePersistenceUnitInfo getPersistenceUnitInfo(String name) {
        return new HibernatePersistenceUnitInfo(name, getEntityClassNames(), getProperties());
    }

    // additional methods
}

次に、EntityManagerFactoryBuilderImplとHibernatePersistenceUnitInfoに必要なパラメーターを提供するメソッドを見てみましょう。

これらのパラメーターには、管理対象エンティティークラス、エンティティークラスの名前、Hibernateの構成プロパティ、およびMysqlDataSourceオブジェクトが含まれます。

public class JpaEntityManagerFactory {
    //...
    
    protected List<String> getEntityClassNames() {
        return Arrays.asList(getEntities())
          .stream()
          .map(Class::getName)
          .collect(Collectors.toList());
    }
    
    protected Properties getProperties() {
        Properties properties = new Properties();
        properties.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
        properties.put("hibernate.id.new_generator_mappings", false);
        properties.put("hibernate.connection.datasource", getMysqlDataSource());
        return properties;
    }
    
    protected Class[] getEntities() {
        return entityClasses;
    }
    
    protected DataSource getMysqlDataSource() {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setURL(DB_URL);
        mysqlDataSource.setUser(DB_USER_NAME);
        mysqlDataSource.setPassword(DB_PASSWORD);
        return mysqlDataSource;
    }
}

簡単にするために、JpaEntityManagerFactoryクラス内にデータベース接続パラメーターをハードコーディングしました。 ただし、本番環境では、これらを別のプロパティファイルに保存する必要があります。

さらに、 getMysqlDataSource()メソッドは、完全に初期化されたMysqlDataSourceオブジェクトを返します。

これは、物事を簡単に追跡できるようにするために行ったものです。 より現実的で緩く結合された設計では、クラス内に作成するのではなく、EntityManagerFactoryBuilderImplのwithDataSource()メソッドを使用してDataSourceオブジェクトを注入します。

4. エンティティマネージャーを使用したCRUD操作の実行

最後に、 JpaEntityManagerFactory インスタンスを使用してJPAエンティティマネージャーを取得し、CRUD操作を実行する方法を見てみましょう。 (簡潔にするために、 User クラスを省略していることに注意してください):

public static void main(String[] args) {
    EntityManager entityManager = getJpaEntityManager();
    User user = entityManager.find(User.class, 1);
    
    entityManager.getTransaction().begin();
    user.setName("John");
    user.setEmail("[email protected]");
    entityManager.merge(user);
    entityManager.getTransaction().commit();
    
    entityManager.getTransaction().begin();
    entityManager.persist(new User("Monica", "[email protected]"));
    entityManager.getTransaction().commit();
 
    // additional CRUD operations
}

private static class EntityManagerHolder {
    private static final EntityManager ENTITY_MANAGER = new JpaEntityManagerFactory(
      new Class[]{User.class})
      .getEntityManager();
}

public static EntityManager getJpaEntityManager() {
    return EntityManagerHolder.ENTITY_MANAGER;
}

5. 結論

この記事では、従来の「persistence.xml」ファイルに依存することなく、JPAのPersistenceUnitInfoインターフェースとHibernateのEntityManagerFactoryBuilderImplクラスのカスタム実装を使用してJPAエンティティマネージャーをプログラムでブートストラップする方法を示しました。

いつものように、この記事に示されているすべてのコードサンプルはGitHubで入手できます。