doma sqlテンプレートのしくみ
DESCRIPTION
Doma SQLテンプレートのしくみTRANSCRIPT
Doma SQLテンプレートのしくみ
by @nakamura_to
@nakamura_to
Domaとは ?
3
DBアクセスライブラリ• コンパイル時のコード生成 & チェック
• 2 Way SQL対応テンプレート
• Java 8対応
• 依存ライブラリなし
デモ
5
DAOインタフェース example/EmployeeDao.java
@Dao(config = AppConfig.class)public interface EmployeeDao {
@Select Employee selectById(Integer id);}
SQLファイル META-INF/example/EmployeeDao/selectById.sql
select *from employeewhere id = /* id */0
コンパイル
• SQLファイルの存在チェック•バインド変数のマッピングチェック• DAO実装クラスの生成
実行
EmployeeDao employeeDao = new EmployeeDaoImpl();Employee result = employeeDao.selectById(1);
select *from employeewhere id = ?
Domaの系譜
10
S2Dao (2004)
DBFlute (2006)
JPA (2006)
S2JDBC (2007)
Doma (2009)
Hibernate 2 (2003)
S2Daoから継承したこと
2 Way SQL 実装のいらないDAO
S2Daoから継承しなかったこと
命名規約 SELECT文の自動生成
2 Way SQLの実装/*BEGIN*/WHERE /*IF job != null*/job = /*job*/'CLERK'/*END*/ /*IF deptno != null*/AND deptno = /*deptno*/20/*END*//*END*/
DBFluteから継承したこと
DateFromTo: 時刻の切捨て/切上げ
DBFluteから継承しなかったこと
ReplaceSchemaなどの周辺ツール
S2JDBCから継承したこと
RDBMSの挙動の違いを抽象化する方法
自動生成系SQLの組み立て方
S2JDBCから継承しなかったもの
ライブラリへの依存 Criteria(流れるインタフェース)
JPAから継承したもの
アノテーション
JPAから継承しなかったもの
永続コンテキスト リレーションシップ 遅延ローディング 専用の問い合わせ言語(JPQL)
なぜ SQLテンプレート を使うべきか?
20
可読性を高くする• StringBuilderと条件分岐でクエリを組み立てるのは煩雑
StringBuilder buf = new StringBuilder();buf.append("select * from emp");if (id != null) { buf.append(" where id = ?");}String sql = buf.toString();
select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
✖
検証しやすくする• StringBuilderで組み立てるとSQLの構文が正しいか検証するのが手間
StringBuilder buf = new StringBuilder();buf.append("select * from emp");if (id != null) { buf.append("where id = ?");}String sql = buf.toString();
select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
✖
SQLインジェクションを防ぐ• 検索条件はPreparedStatementの?にバインド
select * from employee where name = /* e.name */'hoge'
select * from employee where name = ?
StringBuilder buf = new StringBuilder();buf.append("select * from emp");if (id != null) { buf.append(" where id = " + id);}String sql = buf.toString();
✖
メンタルモデルに合わせる• SQLは専用のエディタでDBにつなぎ、小さくちょっとずつ組み立てたい
select * from emp
select * from emp where salary > 1000
select * from emp where salary > 1000 order by name
select name, salary from emp where salary > 1000 order by name
select name, salary from emp where salary > /*e.salary*/0 order by name
Domaの
SQLテンプレートの 何が良いのか?
存在しないプロパティアクセスはコンパイルエラー
select * from emp where salary > /*e.hoge*/0
@SelectList<Employee> selectByExample(Employee e);
条件分岐の間違いはコンパイルエラー
select * from emp where/*%if e.id != null */ id = /* id */0
広いスコープの条件分岐はコンパイルエラー
/*%if e.name == null */select count(*) from emp/*%else */select * from emp where name = /* e.name */'hoge'/*%end *
select * from emp where /*%if e.name == null */
name = /* e.name */'hoge'/*%else */
andname is null
/*%end *
✖
SQL構文木の構築と走査
29
DAOインタフェース
@Dao(config = AppConfig.class) public interface EmpDao {
@Select Emp select(Emp e);
@Select List<Emp> selectList(Emp e); }
パース
走査
構文木
SQLテンプレート select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
マッピングや 式言語の文法 チェック
• eはメソッドパラメータに存在する? • id はEmpに存在する? • if と end は対応している?
コンパイル時
パース
構文木
実行時
走査
SQLテンプレート select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
SQLの生成 orselect * from emp
select * from emp where id = ?
パース
構文木
実行時(ページング)
走査
SQLテンプレートselect * from emp where salary > /* e.salary */0
ページング用 構文木
変換
SQLの生成select * from emp where salary > ?offset 10 limit 100
パース
構文木
実行時(悲観的ロック)SQLテンプレート select * from emp where
id = /* e.id */0
変換
悲観的ロック用 構文木
SQLの生成select * from emp where id = ?for update
走査
なぜ構文木を構築する?
SQLを変換したい コンパイル時と実行時で異なる処理をしたいが、できるだけ共通化したい
RDBMSのSQL方言対応
パース時はRDBMSごとのSQL方言を考慮せず、共通的なノードのみ押さえる 変換(ページング、悲観的ロック)のみを方言ごとに実行できるようにする 新しい方言への対応は容易な作り
構築と変換データ構造
SQLファイル
select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
Anonymous
SelectStatement
WordSpace SpaceWord
from emp
SelectClause WhereClauseFromClause
Word Space Other Space
select *
If End /*%end *//*%if e.id != null */
Word
0
Space Word Space Other BindVariableSpace Space
!=id /* e.id */
IfBlockSpaceWord
where
木の構築
Fragment
Fragmentoffset 10
limit 100
If
Anonymous
SelectStatement
SelectClause WhereClause
IfBlock
End
Word Space Other
FromClause
WordSpace Space SpaceSpace Word
Space Word Space Other BindVariableSpace Space
select * from emp
!=id /* e.id */
/*%end */
Word
0
/*%if e.id != null */
Word
where
木の変換(ページング)
OrderByClause
If
Anonymous
SelectStatement
SelectClause WhereClause
IfBlock
End
Word Space Other
FromClause
WordSpace Space SpaceSpace Word
Space Word Space Other BindVariableSpace Space
select * from emp
!=id /* e.id */
/*%end */
Word
0
/*%if e.id != null */
Word
where
木の変換(悲観的ロック)
ForUpdateClause
for update
Word
スペースを保つ
スタイルの維持 行や列数の報告
式言語の構築と走査
43
Anonymous
SelectStatement
WordSpace SpaceWord
from emp
SelectClause WhereClauseFromClause
Word Space Other Space
select *
If End /*%end *//*%if e.id != null */
Word
0
Space Word Space Other BindVariableSpace Space
!=id /* e.id */
IfBlockSpaceWord
where
NeOperator
FieldOperator Literal
木の構築
idVariable
null
!=
e
e.id != null
FieldOperator
idVariable
e
e.id
• コンパイル時にも実行時にも走査
まとめ
46
まとめ• DomaのSQLテンプレートはコンパイル時チェックが可能
• SQLの構文木と式言語の構文木を構築してチェック
• 新しいRDMBSへの対応は容易
ハックして何か新しい機能を!
参考情報
49
Doma 2 Java 8 時代のDBアクセス
https://nakamura-to.github.io/presentation-doma2-with-java8/
Thank you