Domaでクライテリアのアイデア
動的なSQLをJavaで組み立てる方法を実験的に考えています。いまのところ導入するかどうかはわからないですが、導入するとしても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とかサブクエリもたぶんいけそう。
結果はフラットなエンティティにマッピングするだけの予定です。リレーションシップは表現しません。