さっしーブログ

埼玉県在住のシステムエンジニアです。基本的には技術的な内容を中心に発信していきます。

JavaSEでJPAを使う

f:id:y_saiki:20171025000902j:plain

目次

 
バッチアプリケーションをJavaSEで開発した際に、
JDBCでごちゃごちゃ書きたくなかったので、JPAの導入を試みた。
 

1. まずJPAとは

以下、wikipediaの一部を引用させていただきます
 
> 関係データベースのデータを扱う Java SE および Java EE のアプリケーションを開発するためのJava用フレームワークである。
 
フルネームは「Java Persistence API」というそう....
 

2. エンティティマネージャについて

自分の理解としては、JavaプログラムとDBの間に構築されるエンティティを管理する部分。
基本的にはJavaプログラムからはDBに直接SQLを発行するのではなく、エンティティマネージャに対して操作を行う。
トランザクションが終了した時点でエンティティマネージャとDBが同期されて、DBに反映される。
 
 

3. データベース接続部分

※エンティティマネージャーを作成しています
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * 対象のDBへ接続を行うためのEntityManagerを管理する
 */
public class DataSourceManager {
 
     /** エンティティマネージャファクトリ */
     private EntityManagerFactory entityManagerFactory;
 
     /** エンティティマネージャ */
     private EntityManager entityManager;
 
     /** persistence-unit名の固定部 */
     private String persistenceUnitNameFixedPart;
 
     /** 実行環境 */
     private String env;
 
     /** ロガー */
     private static final Logger LOGGER = LoggerFactory.getLogger("DataSourceManager.class");
 
     /**
     * コンストラクタ
     * @param persistenceUnitNameFixedPart
     * persistence-unit名の固定部
     * @param env
     * 実行環境
     */
     protected DataSourceManager(String persistenceUnitNameFixedPart, String env) {
         this.persistenceUnitNameFixedPart = persistenceUnitNameFixedPart;
         this.env = env.toLowerCase();
     }
 
     /**
     * DBへ接続する
     */
     public void connect() {
         if (this.env.equals(AppConsts.ENV_DEV)) {
              LOGGER.debug("ローカル環境へのコネクションを確立します....");
              createEntityManager();
         } else if (this.env.equals(AppConsts.ENV_TEST)) {
              LOGGER.debug("テスト環境へのコネクションを確立します....");
              createEntityManager();
         } else if (this.env.equals(AppConsts.ENV_PRODUCTION)) {
              LOGGER.debug("本番環境のへのコネクションを確立します....");
              createEntityManager();
         } else {
              LOGGER.debug("コネクション確立失敗....");
              LOGGER.debug("環境変数設定に誤りがあります。");
 
              String errorMsg = SingletonPropertiesLoader.getProperties(AppConsts.PROP_MESSAGES)
              .getProperty([メッセージコード]);
 
               //例外を発生させる
         }
 
         LOGGER.debug("コネクション確立完了");  
     }
 
     /**
     * EntityManagerインスタンスの生成
     * @param dataSource
     * @param env
     */
     synchronized private void createEntityManager() {
 
         String persistenceUnitName = getPersistenceUnitName();
 
         LOGGER.debug("PersistenceUnitName: " + persistenceUnitName);
 
         if (this.entityManagerFactory == null) {
              // EntityManagerFactoryのみしか開いていない場合
              entityManagerFactory = Persistence.createEntityManagerFactory(persistenceUnitName);
              entityManager = entityManagerFactory.createEntityManager();
         }
 
         if (this.entityManagerFactory != null && this.entityManager == null) {
              // EntityManagerFactoryは開いているがとEntityManagerが開いていない場合
              entityManager = entityManagerFactory.createEntityManager();
         }
     }
 
     /**
     * @param dataSource
     * @param env
     * @return
     */
     private String getPersistenceUnitName() {
         return this.persistenceUnitNameFixedPart + "_" + this.env;
     }
 
     /**
     * EntityManagerを取得する
     * @return entityManager
     */
     public EntityManager getEntityManager() {
         return entityManager;
     }
 
     /**
     * DBコネクションを閉じる
     */
     synchronized public void close() {
 
         if (this.entityManager != null) {
              if (this.entityManager.isOpen()) {
                   entityManager.close();
                   LOGGER.debug("EntityManagerクローズ完了");
              }
         }
 
         if (this.entityManager != null) {
              if (entityManagerFactory.isOpen()) {
                   entityManagerFactory.close();
                   LOGGER.debug("EntityManagerFactoryクローズ完了");
              }
         }
     }
}
 

4.上記クラスを継承して作成したMySQL操作を行うクラス

※実装者に使いやすいようにjpaの実装をラップしている
import java.util.List;
import javax.persistence.EntityExistsException;
import javax.persistence.TransactionRequiredException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * DB操作するための機能を管理する
 */
public class MySqlOperator extends DataSourceManager {
 
     private static final String PERSISTENCE_UNIT = "open_platform_gateway_batch_mysql";
 
     /** ロガー */
     private static final Logger LOGGER = LoggerFactory.getLogger("MySqlConnector.class");
 
     /**
     * コンストラクタ
     */
     protected MySqlOperator() throws BatchApplicationException {
         super(PERSISTENCE_UNIT, System.getenv("GNAVIENV"));
     }
 
     /**
     * DBへInsert文を発行する
     * @param entity
     * @throws EntityExistsException
     * エンティティがすでに存在する場合
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     * @throws TransactionRequiredException
     * トランザクションが存在しない場合
     */
     public <T> void insert(T entity)
         throws EntityExistsException, IllegalArgumentException, TransactionRequiredException {
           getEntityManager().getTransaction().begin();
            getEntityManager().persist((T) entity);
            getEntityManager().getTransaction().commit();
     }
 
     /**
     * DBへUpdate文を発行する
     * @param entity
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     * @throws TransactionRequiredException
     * トランザクションが存在しない場合
     */
     public <T> void update(T entity) throws IllegalArgumentException, TransactionRequiredException {
         getEntityManager().getTransaction().begin();
         getEntityManager().merge(
             (T) entity
         );
         getEntityManager().getTransaction().commit();
     }
 
     /**
     * @param entity
     * @return
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     */
     public <T> T select(T entity) throws IllegalArgumentException {
         T getEntity = (T) getEntityManager().find(entity.getClass(), (T) entity);
         return getEntity;
     }
 
     /**
     * @param entity
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     * @throws TransactionRequiredException
     * トランザクションが存在しない場合
     */
     public <T> void delete(T entity) throws IllegalArgumentException, TransactionRequiredException {
           getEntityManager().getTransaction().begin();
            getEntityManager().remove(
                (T) entity
            );
            getEntityManager().getTransaction().commit();
     }
 
     /**
     * DBへInsert文を発行する
     * @param entity
     * @throws EntityExistsException
     * エンティティがすでに存在する場合
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     * @throws TransactionRequiredException
     * トランザクションが存在しない場合
     */
     public <T> void insert(List<T> entities)
         throws EntityExistsException, IllegalArgumentException, TransactionRequiredException {
         getEntityManager().getTransaction().begin();
         entities.forEach(entity -> getEntityManager().persist(
             (T) entity)
         );
         getEntityManager().getTransaction().commit();
     }
 
     /**
     * DBへUpdate文を発行する
     * @param entity
     * @throws IllegalArgumentException
     * インスタンスがエンティティでない場合
     * @throws TransactionRequiredException
     * トランザクションが存在しない場合
     */
     public <T> void update(List<T> entities) throws IllegalArgumentException, TransactionRequiredException {
         getEntityManager().getTransaction().begin();
         entities.forEach(entity -> getEntityManager().merge(
             (T) entity)
         );
         getEntityManager().getTransaction().commit();
     }
}
 
 

5.シングルトンでMySQLのコネクターを管理する

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
 * MySqlへのコネクションをシングルトンで管理する
 */
public class SingletonMySqlConnector {
 
     /** MySqlOperatorインスタンス */
     private static MySqlOperator singletonMySqlOperator = new MySqlOperator();
 
     /** ロガー */
     private static final Logger LOGGER = LoggerFactory.getLogger("SingletonMySqlConnector.class");
 
     /**
     * 不可視コンストラクタ
     */
     private SingletonMySqlConnector() {
     }
 
     /**
     * MySqlOperatorインスタンスを取得する
     * @return
     */
     public static MySqlOperator getInstance() {
          return singletonMySqlOperator;
     }
 
}
 
 
 

6.設定ファイル(persistence.xml)

※上記のコードは環境変数を用いて、開発環境、テスト環境、本番環境用に出し分けているため、必要ない場合は上記のコードのenv変数を削除してソースコードを修正する必要あり。
 
※読み込ませるには以下のディレクトリ構造でなくてはいけない。
 
※設定ファイルの読み込みは自動で、mavenのディレクトリ構造で捜査して設定ファイルを探すため以下の構成でなくてはいけないだとか。
 
./resources/META-INF/persistence.xml
 
<!-- MySQL設定 -->
  <persistence-unit name="sai_mysql" transaction-type="RESOURCE_LOCAL">
 
  eclipselink-2.6.4.jar内のPersistenceProviderクラスをフルパスで指定
  <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
 
   <class>com.example.entity.SampleEntity</class>
 
  <properties>
  <!-- 開発用DBのため、SSL接続を無効に設定している -->
  <property name="javax.persistence.jdbc.url" value="jdbc:mysql://[IPアドレス]:[接続ポート]/[DB名]?useSSL=false" />
  <property name="javax.persistence.jdbc.user" value="[ユーザー名]"/>
  <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
  <property name="javax.persistence.jdbc.password" value="[パスワード]"/>
 
  ※以下を設定しておくと生成されたSQLをログに出力してくれる
  <property name="eclipselink.logging.level.sql" value="fine"/>
 
  ※なんの設定か忘れましたが確か必要でした。
  <property name="toplink.platform.class.name" value="oracle.toplink.essentials.platform.database.DerbyPlatform" />
  </properties>
  </persistence-unit>
</persistence>
 

7.エンティティクラスの作成

「jpa エンティティクラス」とかで調べれば出てくるのでここは割愛します。
 

8.必要jar

今回のjpaでのmysqlとのマッピングを行うために取り込んだjarは以下になります。
mavenのリポジトリとかからjarをダウンロードできるので、各自で揃えてください。
 
※jarの参照順に注意すること、以下の順番で参照していれば問題なし。
 
・mysql-connector-java-5.1.39-bin.jar
・javax.persistence-2.1.1.jar
・toplink-essentials-2.1-60.jar
・eclipselink-2.6.4.jar
・validation-api-1.1.0.Final.jar
 
 
以上