2011年5月12日木曜日

eclipselink を使う :(2)CRUD操作用クラスの作成

サンプルアプリケーションのデータベース処理プラグインでエンティティクラス(連絡先、住所、電話番号)を作成しました。同じプラグインに、CRUD操作のためのクラスも作成します。

CRUD操作用のクラスを作成する
データベース処理プラグインに新規パッケージ com.itrane.mycontact.db.home を作成します。このパッケージに、連絡先、住所、電話番号の各エンティティに対するデータ処理用のクラス ContactHome, AddressHome, PhoneHome を作成します。 エンティティの場合と同様に各クラスに共通の機能を持つスーパークラスをパッケージ com.itrane.common.db に作成します:
public abstract class AbstractEntityHome<E> extends AbstractHome<E> {

/**
* コンストラクタ.
* @param type  エンティティの型
*/
public AbstractEntityHome(Class<E> type) {
  super(type);
}

/**
* 指定条件に合致するエンティティのリストを取得.
* @param query  クエリ
* @return 結果リスト
*/
@SuppressWarnings("unchecked")
public List<E> findByQuery(String query) {
  entityList = LocalJPA.getEntityManager().createQuery(query)
          .getResultList();
  return entityList;
}

/**
* 全件取得
*/
public List<E> findAll() {
  String qry = "select t from " + type.getSimpleName() + " t order by t.id";
  return findByQuery(qry);
}

public List<E> findAll(String orderColumn) {
  String qry = "select t from " + type.getSimpleName() + " t order by t." + orderColumn;
  return findByQuery(qry);
}

/**
* 指定IDに合致するエンティティを取得.
* @param id  ID
* @return 結果
*/
public E findById(Long id) throws Exception {
  //instance = JpaUtils.getEntityManager().find(type, id);
  instance = LocalJPA.getEntityManager().find(type, id);
  return instance;
}

/**
* エンティティを追加する.
* @param entity   追加エンティティ
*/
public void persist(E entity, boolean notify) throws Exception {
  LocalJPA.beginOrJoinTransaction();
  instance = entity;
  LocalJPA.getCurrentEntityManager().persist(instance); //DBテーブルへ追加

  if (notify)
      getListeners().firePropertyChange(ADD_ENTITY, null, instance);
}

public void persistAndCommit(E entity) throws Exception {
  persist(entity, true);
  LocalJPA.commitAndClose();
}

/**
* エンティティを新規作成する
*/
@SuppressWarnings("unchecked")
public void create() {
  if (type != null) {
      try {
          instance = (E)type.getClass().newInstance();
      } catch (Exception e) {
          throw new RuntimeException(e);
      }
  }
}

/**
* 更新.
* @param entity  更新エンティティ
*/
public void update(E entity, boolean notify) throws Exception {
  LocalJPA.beginOrJoinTransaction();
  instance = LocalJPA.getCurrentEntityManager().merge(entity);

  if (notify)
      getListeners().firePropertyChange(UPD_ENTITY, null, instance);
}

public void updateAndCommit(E entity) throws Exception {
  update(entity, true);
  LocalJPA.commitAndClose();
}

/**
* 削除.
* @param entity   削除エンティティ
*/
public void remove(E entity, boolean notify) throws Exception {
  LocalJPA.beginOrJoinTransaction();
  EntityManager em = LocalJPA.getCurrentEntityManager();
  instance = entity;
  em.remove(em.merge(instance));    //DBテーブルから削除

  if (notify)
      getListeners().firePropertyChange(DEL_ENTITY, instance, null);
}

public void removeAndCommit(E entity) throws Exception {
  remove(entity, true);
  LocalJPA.commitAndClose();
}

public void setInstance(E instance) {
  this.instance = instance;
}
public E getInstance() {
  return instance;
}
public void fireInputChange() {
  getListeners().firePropertyChange(INPUT_CHANGE, instance, null);
}
}
上記クラスでは、エンティティマネージャの取得と、トランザクション管理のためのユーティリティクラス LocalJPA を参照しています。このユーティリティークラスを com.itrane.common.db に作成します:
public class LocalJPA {

private static ThreadLocal<EntityManager> entityManagerHolder = new ThreadLocal<EntityManager>();
private static ThreadLocal<EntityTransaction> transactionHolder = new ThreadLocal<EntityTransaction>();
private static EntityManagerFactory entityManagerFactory;

/**
* エンティティマネージャ・ファクトリを取得.
* @return エンティティマネージャファクトリ
*/
public static EntityManagerFactory getEntityManagerFactory() {
   if (entityManagerFactory==null) {
       entityManagerFactory = Persistence.createEntityManagerFactory("testpu");
   }
   return entityManagerFactory;
}

public static void setEntityManagerFactory(String pu, Map<String, Object> properties) {
   entityManagerFactory = new PersistenceProvider().createEntityManagerFactory(pu, properties);
}
public static void setEntityManagerFactory(EntityManagerFactory emf) {
   entityManagerFactory = emf;
}

/**
* フォルダから生成済みのエンティティマネージャを取得する.
* @return 生成済みエンティティマネージャ
*/
public static EntityManager getCurrentEntityManager() {
   return entityManagerHolder.get();
}

/**
* エンティティマネージャを取得する.
* まだ生成されていない場合は、生成して、フォルダにセットする.
* @return エンティティマネージャ
*/
public static EntityManager getEntityManager() {
   EntityManager entityManager = entityManagerHolder.get();
   if (entityManager == null || !entityManager.isOpen()) {
       entityManager = getEntityManagerFactory().createEntityManager();
       entityManagerHolder.set(entityManager);
   }
   return entityManager;
}

/**
* エンティティマネージャ・フォルダとトランザクション・フォルダを空にする.
*/
public static void cleanup() {
   EntityManager entityManager = entityManagerHolder.get();
   EntityTransaction transaction = transactionHolder.get();

   if (transaction != null && transaction.isActive()) {
       if (transaction.getRollbackOnly()) {
           transaction.rollback();
       }
   }
   if (entityManager != null && entityManager.isOpen()) {
       entityManager.close();
   }
   entityManagerHolder.set(null);
   transactionHolder.set(null);
}

/**
* 実行中のトランザクションのコミットとエンティティマネージャのクローズを行う.
* @throws Exception
*/
public static void commitAndClose()
       throws Exception {
   EntityManager entityManager = entityManagerHolder.get();
   EntityTransaction transaction = transactionHolder.get();

   if (transaction != null && transaction.isActive()) {
       try {
           transaction.commit();
       } catch (Exception ex) {
           try {
               if (transaction.isActive())
                   transaction.rollback();
           } catch (Exception ex2) {
               //DBActivator.log("コミットエラーで、ロールバックに失敗", ex2);
               throw ex; // 元の例外をスローする
           }
           if (ex.getMessage().indexOf("Error Code: 1062")>0) {
               throw new DuplicateEntryException("重複エントリー");
           }
           throw ex;
       }
   }
   if (entityManager != null && entityManager.isOpen()) {
       entityManager.close();
   }
   entityManagerHolder.set(null);
   transactionHolder.set(null);
}

/**
* エンティティマネージャの生成とトランザクションの開始. 
*/
public static void beginOrJoinTransaction()   throws Exception {
   getTransaction(getEntityManager());
}

/**
* トランザクションの取得または生成&開始.
* @return トランザクション
*/
private static EntityTransaction getTransaction(
       EntityManager entityManager) {

   // トランザクションフォルダにトランザクションが存在するかチェックする
   EntityTransaction transaction = transactionHolder.get();
   if (transaction == null || !transaction.isActive()) {
       // もしフォルダになければ、トランザクションを生成して、開始する
       transaction = entityManager.getTransaction();
       transaction.begin();
       transactionHolder.set(transaction);
   } else {
       //実行中のトランザクションにジョインする
   }
   return transaction;
}
}
AbstractEntityHome を拡張して、AddressHome, PhoneHome, ContactHome を作成します。ほとんどの機能はスーパークラスが行うので、サブクラスで記述するコードはほとんどありません。
住所ホーム:
public class AddressHome extends AbstractEntityHome<Address> {

public AddressHome(Class<Address> type) {
    super(type);
}
}


電話番号ホーム:
public class PhoneHome extends AbstractEntityHome<Phone> {

public PhoneHome(Class<Phone> type) {
    super(type);
}
}

連絡先ホームには、住所と電話番号を扱うためのメソッドを追加します:
public class ContactHome extends AbstractEntityHome<Contact> {

 private AddressHome addressHome;
 private PhoneHome phoneHome;

 public ContactHome(Class<Contact> type) {
     super(type);
 }

 public ContactHome(Class<Contact> type,
         AddressHome addressHome, PhoneHome phoneHome) {
     this(type);
     this.addressHome = addressHome;
     this.phoneHome = phoneHome;
 }

 public void add(Contact contact,
         Address[] addresses, Phone[] phones) throws Exception {
     for (Address a: addresses) {
         contact.getAddresses().put(a.getAddressType(), a);
     }
     for (Phone p: phones) {
         contact.getPhones().put(p.getPhoneType(), p);
     }
     persist(contact, false);
 }

 public void addAndCommit(Contact contact,
         Address[] addresses, Phone[] phones) throws Exception {
     add(contact, addresses, phones);
     LocalJPA.commitAndClose();
 }
}


他のプラグインから、これらのクラスを使用できるように、MANIFEST.MF のランタイムページで以下のパッケージをエクスポートします。
  • com.itrane.common.db
  • com.itrane.mycontact.db
  • com.itrane.mycontact.db.entity
  • com.itrane.mycontact.db.home

0 件のコメント: