Javaでプログラム的にJPAをブートストラップする

JavaでのプログラムによるJPAのブートストラップ

1. 概要

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

ここでのアプローチはprovides a centralized mechanism for configuring one or more persistence units と関連するpersistence contextsです。

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

明るい面では、it’s possible to bootstrap a JPA implementation without resorting to the “persistence.xml” file at all, by just using plain Javaです。

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

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

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

Using all the data gathered by parsing the “persistence.xml” file, t永続性プロバイダーは、この実装を使用してエンティティマネージャーファクトリを作成します。 このファクトリから、エンティティマネージャを取得できます。

Since we won’t rely on the “persistence.xml” file, the first thing that we need to do is to provide our own PersistenceUnitInfo implementation.永続性プロバイダーにはHibernateを使用します。

public class HibernatePersistenceUnitInfo implements PersistenceUnitInfo {

    public static String JPA_VERSION = "2.1";
    private String persistenceUnitName;
    private PersistenceUnitTransactionType transactionType
      = PersistenceUnitTransactionType.RESOURCE_LOCAL;
    private List managedClassNames;
    private List mappingFileNames = new ArrayList<>();
    private Properties properties;
    private DataSource jtaDataSource;
    private DataSource nonjtaDataSource;
    private List transformers = new ArrayList<>();

    public HibernatePersistenceUnitInfo(
      String persistenceUnitName, List managedClassNames, Properties properties) {
        this.persistenceUnitName = persistenceUnitName;
        this.managedClassNames = managedClassNames;
        this.properties = properties;
    }

    // standard setters / getters
}

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

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

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

Hibernate makes this process a breeze, with its EntityManagerFactoryBuilderImpl class (a neat implementation of the builder pattern)

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

まず、Hibernate’s EntityManagerFactoryBuilderImplクラスとHibernatePersistenceUnitInfoクラスを使用して、creating an entity manager factory and an entity managerを処理するメソッドを紹介しましょう。

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 configuration = new HashMap<>();
        return new EntityManagerFactoryBuilderImpl(
          new PersistenceUnitInfoDescriptor(persistenceUnitInfo), configuration)
          .build();
    }

    protected HibernatePersistenceUnitInfo getPersistenceUnitInfo(String name) {
        return new HibernatePersistenceUnitInfo(name, getEntityClassNames(), getProperties());
    }

    // additional methods
}

次に、methods that provide the parameters required by EntityManagerFactoryBuilderImpl and HibernatePersistenceUnitInfoを見てみましょう。

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

public class JpaEntityManagerFactory {
    //...

    protected List 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オブジェクトを返します。

これは、物事を簡単に追跡できるようにするために行ったものです。 より現実的で疎結合の設計では、inject a DataSource object using EntityManagerFactoryBuilderImpl's withDataSource() method, rather than creating it within the classになります。

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” file.に依存することなく、JPAのPersistenceUnitInfo interface and Hibernate’s EntityManagerFactoryBuilderImpl classのカスタム実装を使用してJPAエンティティマネージャーをプログラムでブートストラップする方法を示しました。

いつものように、この記事に示されているすべてのコードサンプルは利用可能ですover on GitHub.