Domaでクライテリアのアイデア

動的なSQLJavaで組み立てる方法を実験的に考えています。いまのところ導入するかどうかはわからないですが、導入するとしてもextensionとして別jarにするつもりです。

SQLを組み立てる場所は、Daoから委譲される先がいいかなと思っています。Domaでは@Delegateで委譲できます。

@Dao(config = AppConfig.class)
public interface EmpDao {
    @Delegate(to = EmpDaoDelegate.class)
    Emp selectById(Integer id);
}

委譲先では、QuerySupport(仮)という名前のクラスを継承します。

public class EmpDaoDelegate extends QuerySupport {

    public EmpDaoDelegate(Config config) {
        super(config);
    }

    public Emp selectById(Integer id) {
        _Emp e = _Emp.newInstance();
        return select(e).from(e).where(eq(e.id, id)).getResult();
    }
}

QuerySupportでは、SQLの組み立てに必要なDSLなメソッドを提供します。これは、ファウラーがいうところのObject Scopingというパターンです。上の例だと、selectとeqを提供しています。

_Empは、Empエンティティのメタクラスです。これはaptで生成されるのですが(今のバージョンでも生成してます)、テーブル名やプロパティの情報をもっているので、これを使ってSQLを組み立てるわけです。プロパティの情報には、カラム名はもちろん型情報も含まれているのでタイプセーフに記述できます。たとえば、eq(e.id, id)としていますが、e.idがIntegerの型パラメータをもっているので、idもIntegerじゃないとコンパイルエラーになります。
上のコードでは次のようなSQLが生成されます。

select _t0.ID, _t0.NAME, _t0.SALARY from EMP _t0 where _t0.ID = ?


Joinとかも考えています。

_Emp e = _Emp.newInstance();
_Dept d = _Dept.newInstance();

List<Emp> results = 
         select(e)
         .from(e)
         .leftOuterJoin(d)
         .on(eq(e.deptId, d.id))
         .where(eq(d.location, "NEW YORK"))
         .getResultList();

SQLはこんな感じ。

select _t0.ID, _t0.NAME, _t0.SALARY from EMP _t0 left outer join DEPT _t1 on _t0.DEPT_ID = _t1.ID where _t1.LOCATION = ?

GROUP BYとかサブクエリもたぶんいけそう。

結果はフラットなエンティティにマッピングするだけの予定です。リレーションシップは表現しません。