JPAでGenericDao - その2

前回の続きです。

前回ではGenericDaoにより単純なCRUDを実装しましたが、これだけでは到底実際のプロジェクトは使えません。特に以下の2つが必要になると思います。

  • 条件指定のSELECT文の発行
  • JoinするSELECT文の発行

この2つを実現するためにまずJPQLを実行するコールバック用インターフェースを作成します。
こんな感じ。

public interface JpaCallback {
    <T> T execute(EntityManager em);
}

そしてIGenericDaoのインターフェースにメソッドを1つ追加します。
こんな感じ。

public interface IGenericDao<E extends IEntity,PK extends Serializable> {
    
    E findByPrimaryKey(PK primaryKey);

    void persist(E entity);
    
    void merge(E entity);

    void remove(E entity);

    /** これを追加 **/
    <T> T executeJpql(JpaCallback callback);
}

そしてGenericDaoJpaでの実装はこんな感じ

public class GenericDaoJpa<E extends IEntity, PK extends Serializable> 
                                          implements IGenericDao<E,PK> {

    private EntityManager em;
    private Class<E> clazz;
    
    //CRUDメソッドは前回と同じなので省略

    @Override
    public <T> T executeJpql(JpaCallback callback) {
        return (T)callback.execute(em);
    }
}

これで終了です。
実際にどういう風に使うかというと
こんな感じ

public class EmployeeByName implements JpaCallback{

    private String employeeName;
    
    public EmployeeByName(String employeeName) {
        this.employeeName = employeeName;
    }
    
    @Override
    public <T> T execute(EntityManager em) {
        return (T)em.createQuery("FROM Employee e " +
                                 "WHERE e.employeeName = :employeeName").
                                 setParameter("employeeName", employeeName).
                                 getSingleResult();
    }
}

JpaCallbackクラスはJPQLの生成及び実行の責務を担います。
そしてコンストラクタにてJPQL生成時に必要な情報を受け取ることでJPQL生成用のオブジェクトとしてビジネスロジックから分離されたクラスを作成することができます。あとはこのクラスのインスタンスをClient側で生成し、メソッドの引数として渡せばOKです。
こんな感じ。

public class GenericDaoMain {

    public static void main(String[] args) {

        //省略・・

        IGenericDao<Employee, Integer> employeeDao = 
                                         factory.get(Employee.class);
        Employee employee = 
               employeeDao.executeJpql(new EmployeeByName("test1"));
        System.out.println(employee);
    }
}

こうすることで冒頭に上げた2点を解決することができました。
コールバック用のクラスを定義する事のメリットとしては

  • GenericDaoを使ったまま複雑なSELECT文を構築可能。
  • JPQL生成のクラスをビジネスロジックから分離できる。
  • JPQL生成に特化したクラスを作成できる。
  • JPQL生成に共通する処理等は親クラスを作成し共通化可能。

といった感じでしょうか。

逆にデメリットとしては

  • JPQL作成用のクラスが膨大にできる。
  • JPQL(SQL)がハードコーディングされる。
  • JPQLが嫌いな人にはとてもじゃないが受け入れられないww

こんな感じですがいかがでしょうか?
次回はSpringを使う場合にどうするかという点でエントリーを書きたいと思います。