SlideShare a Scribd company logo
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 
employee 
where 
id = /* id */0
コンパイル 
• SQLファイルの存在チェック 
• バインド変数のマッピングチェック 
• DAO実装クラスの生成
実行 
EmployeeDao employeeDao = new EmployeeDaoImpl(); 
Employee result = employeeDao.selectById(1); 
select 
* 
from 
employee 
where 
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の?にバインド 
StringBuilder buf = new StringBuilder(); 
buf.append("select * from emp"); 
if (id != null) { 
buf.append(" where id = " + id); 
} 
String sql = buf.toString(); 
select * from employee where 
name = /* e.name */'hoge' 
select * from employee where 
name = ? 
✖
メンタルモデルに合わせる 
• 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 
List<Employee> selectByExample(Employee e); 
select * from emp where salary > /*e.hoge*/0
条件分岐の間違いはコンパイ 
ルエラー 
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 */ 
and 
name 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 */ 
select * from emp where 
id = ? 
SQLの生成or 
select * from emp
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 
SelectClause FromClause WhereClause 
Word Space Word Space 
from emp 
Word Space Other Space 
select * 
Word Space IfBlock 
where 
/*%if e.id != null */ If End /*%end */ 
Space Word Space Other Space BindVariable Space 
Word 
0 
id != /* e.id */ 
木の構築
Fragment 
offset 10 
Fragment 
limit 100 
If 
Anonymous 
SelectStatement 
SelectClause WhereClause 
IfBlock 
End 
Word Space Other 
FromClause 
Space Word Space Word Space Space 
select * from emp 
Space Word Space Other Space BindVariable Space 
id != /* e.id */ 
/*%end */ 
Word 
0 
/*%if e.id != null */ 
Word 
where 
木の変換(ページング) 
OrderByClause
If 
Anonymous 
SelectStatement 
SelectClause WhereClause 
IfBlock 
End 
Word Space Other 
FromClause 
Space Word Space Word Space Space 
select * from emp 
Space Word Space Other Space BindVariable Space 
id != /* e.id */ 
/*%end */ 
Word 
0 
/*%if e.id != null */ 
Word 
where 
木の変換(悲観的ロック) 
ForUpdateClause 
Word 
for update
スペースを保つ 
スタイルの維持 
行や列数の報告
式言語の構築と走査 
43
Anonymous 
SelectStatement 
SelectClause FromClause WhereClause 
Word Space Word Space 
from emp 
Word Space Other Space 
select * 
Word Space IfBlock 
where 
/*%if e.id != null */ If End /*%end */ 
Space Word Space Other Space BindVariable Space 
Word 
0 
id != /* e.id */
e.id != null 
NeOperator 
木の構築 
FieldOperator Literal 
id 
Variable 
null 
!= 
e 
FieldOperator 
id 
Variable 
e 
e.id 
• コンパイル時にも実行時にも走査
まとめ 
46
まとめ 
• DomaのSQLテンプレートはコンパイル時 
チェックが可能 
• SQLの構文木と式言語の構文木を構築して 
チェック 
• 新しいRDMBSへの対応は容易
ハックして何か新しい機能を!
参考情報 
49
GitHub Repository 
https://github.com/domaframework/doma
Doma 2 
Java 8 時代のDBアクセス 
https://nakamura-to.github.io/presentation-doma2- 
with-java8/
Thank you

More Related Content

Doma SQLテンプレートのしくみ