Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
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 se...
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 
emplo...
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 de...
DBFluteから継承したこと 
DateFromTo: 時刻の切捨て/切上げ
DBFluteから継承しなかったこと 
ReplaceSchemaなどの周辺ツール
S2JDBCから継承したこと 
RDBMSの挙動の違いを抽象化する方法 
自動生成系SQLの組み立て方
S2JDBCから継承しなかったもの 
ライブラリへの依存 
Criteria(流れるインタフェース)
JPAから継承したもの 
アノテーション
JPAから継承しなかったもの 
永続コンテキスト 
リレーションシップ 
遅延ローディング 
専用の問い合わせ言語(JPQL)
なぜ 
SQLテンプレート 
を使うべきか? 
20
可読性を高くする 
• StringBuilderと条件分岐でクエリを組み立て 
るのは煩雑 
StringBuilder buf = new StringBuilder(); 
buf.append("select * from emp");...
検証しやすくする 
• StringBuilderで組み立てるとSQLの構文が正 
しいか検証するのが手間 
StringBuilder buf = new StringBuilder(); 
buf.append("select * from...
SQLインジェクションを防ぐ 
• 検索条件はPreparedStatementの?にバインド 
StringBuilder buf = new StringBuilder(); 
buf.append("select * from emp")...
メンタルモデルに合わせる 
• SQLは専用のエディタでDBにつなぎ、小さく 
ちょっとずつ組み立てたい 
select * from emp 
select * from emp where salary > 1000 
select * f...
Domaの 
SQLテンプレートの 
何が良いのか?
存在しないプロパティアクセ 
スはコンパイルエラー 
@Select 
List<Employee> selectByExample(Employee e); 
select * from emp where salary > /*e.hoge...
条件分岐の間違いはコンパイ 
ルエラー 
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...
SQL構文木の構築と走査 
29
DAOインタフェース 
@Dao(config = AppConfig.class) 
public interface EmpDao { 
@Select 
Emp select(Emp e); 
@Select 
List<Emp> sel...
SQLテンプレートselect * from emp where 
パース 
構文木 
走査 
/*%if e.id != null*/ 
id = /* e.id */0 
/*%end */ 
マッピングや 
式言語の文法 
チェック 
•...
SQLテンプレートselect * from emp where 
パース 
構文木 
実行時 
走査 
/*%if e.id != null*/ 
id = /* e.id */0 
/*%end */ 
select * from emp ...
SQLテンプレートselect * from emp where 
パース 
構文木 
実行時(ページング) 
変換 
走査 
salary > /* e.salary */0 
ページング用 
構文木 
SQLの生成 
select * fr...
パース 
構文木 
実行時(悲観的ロック) 
SQLテンプレートselect * from emp where 
id = /* e.id */0 
変換 
悲観的ロック用 
構文木 
SQLの生成 
select * from emp whe...
なぜ構文木を構築する? 
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 
...
Fragment 
offset 10 
Fragment 
limit 100 
If 
Anonymous 
SelectStatement 
SelectClause WhereClause 
IfBlock 
End 
Word Spa...
If 
Anonymous 
SelectStatement 
SelectClause WhereClause 
IfBlock 
End 
Word Space Other 
FromClause 
Space Word Space Wor...
スペースを保つ 
スタイルの維持 
行や列数の報告
式言語の構築と走査 
43
Anonymous 
SelectStatement 
SelectClause FromClause WhereClause 
Word Space Word Space 
from emp 
Word Space Other Space 
...
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
Upcoming SlideShare
Loading in …5
×

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

15,286 views

Published on

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

Published in: Technology
  • Be the first to comment

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

  1. 1. Doma SQLテンプレートのしくみ by @nakamura_to
  2. 2. @nakamura_to
  3. 3. Domaとは ? 3
  4. 4. DBアクセスライブラリ • コンパイル時のコード生成 & チェック • 2 Way SQL対応テンプレート • Java 8対応 • 依存ライブラリなし
  5. 5. デモ 5
  6. 6. DAOインタフェース example/EmployeeDao.java @Dao(config = AppConfig.class) public interface EmployeeDao { @Select Employee selectById(Integer id); }
  7. 7. SQLファイル META-INF/example/EmployeeDao/selectById.sql select * from employee where id = /* id */0
  8. 8. コンパイル • SQLファイルの存在チェック • バインド変数のマッピングチェック • DAO実装クラスの生成
  9. 9. 実行 EmployeeDao employeeDao = new EmployeeDaoImpl(); Employee result = employeeDao.selectById(1); select * from employee where id = ?
  10. 10. Domaの系譜 10
  11. 11. S2Dao (2004) DBFlute (2006) JPA (2006) S2JDBC (2007) Doma (2009) Hibernate 2 (2003)
  12. 12. S2Daoから継承したこと 2 Way SQL 実装のいらないDAO
  13. 13. S2Daoから継承しなかったこと 命名規約 SELECT文の自動生成 2 Way SQLの実装 /*BEGIN*/WHERE /*IF job != null*/job = /*job*/'CLERK'/*END*/ /*IF deptno != null*/AND deptno = /*deptno*/20/*END*/ /*END*/
  14. 14. DBFluteから継承したこと DateFromTo: 時刻の切捨て/切上げ
  15. 15. DBFluteから継承しなかったこと ReplaceSchemaなどの周辺ツール
  16. 16. S2JDBCから継承したこと RDBMSの挙動の違いを抽象化する方法 自動生成系SQLの組み立て方
  17. 17. S2JDBCから継承しなかったもの ライブラリへの依存 Criteria(流れるインタフェース)
  18. 18. JPAから継承したもの アノテーション
  19. 19. JPAから継承しなかったもの 永続コンテキスト リレーションシップ 遅延ローディング 専用の問い合わせ言語(JPQL)
  20. 20. なぜ SQLテンプレート を使うべきか? 20
  21. 21. 可読性を高くする • 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 */ ✖
  22. 22. 検証しやすくする • 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 */ ✖
  23. 23. 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 = ? ✖
  24. 24. メンタルモデルに合わせる • 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
  25. 25. Domaの SQLテンプレートの 何が良いのか?
  26. 26. 存在しないプロパティアクセ スはコンパイルエラー @Select List<Employee> selectByExample(Employee e); select * from emp where salary > /*e.hoge*/0
  27. 27. 条件分岐の間違いはコンパイ ルエラー select * from emp where /*%if e.id != null */ id = /* id */0
  28. 28. 広いスコープの条件分岐はコ ンパイルエラー /*%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 * ✖
  29. 29. SQL構文木の構築と走査 29
  30. 30. DAOインタフェース @Dao(config = AppConfig.class) public interface EmpDao { @Select Emp select(Emp e); @Select List<Emp> selectList(Emp e); }
  31. 31. SQLテンプレートselect * from emp where パース 構文木 走査 /*%if e.id != null*/ id = /* e.id */0 /*%end */ マッピングや 式言語の文法 チェック • eはメソッドパラメータに存在する? • id はEmpに存在する? • if と end は対応している? コンパイル時
  32. 32. SQLテンプレートselect * from emp where パース 構文木 実行時 走査 /*%if e.id != null*/ id = /* e.id */0 /*%end */ select * from emp where id = ? SQLの生成or select * from emp
  33. 33. SQLテンプレートselect * from emp where パース 構文木 実行時(ページング) 変換 走査 salary > /* e.salary */0 ページング用 構文木 SQLの生成 select * from emp where salary > ? offset 10 limit 100
  34. 34. パース 構文木 実行時(悲観的ロック) SQLテンプレートselect * from emp where id = /* e.id */0 変換 悲観的ロック用 構文木 SQLの生成 select * from emp where id = ? for update 走査
  35. 35. なぜ構文木を構築する? SQLを変換したい コンパイル時と実行時で異なる処理をしたい が、できるだけ共通化したい
  36. 36. RDBMSのSQL方言対応 パース時はRDBMSごとのSQL方言を考慮せ ず、共通的なノードのみ押さえる 変換(ページング、悲観的ロック)のみを方 言ごとに実行できるようにする 新しい方言への対応は容易な作り
  37. 37. 構築と変換 データ構造
  38. 38. SQLファイル select * from emp where /*%if e.id != null*/ id = /* e.id */0 /*%end */
  39. 39. 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 */ 木の構築
  40. 40. 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
  41. 41. 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
  42. 42. スペースを保つ スタイルの維持 行や列数の報告
  43. 43. 式言語の構築と走査 43
  44. 44. 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 */
  45. 45. e.id != null NeOperator 木の構築 FieldOperator Literal id Variable null != e FieldOperator id Variable e e.id • コンパイル時にも実行時にも走査
  46. 46. まとめ 46
  47. 47. まとめ • DomaのSQLテンプレートはコンパイル時 チェックが可能 • SQLの構文木と式言語の構文木を構築して チェック • 新しいRDMBSへの対応は容易
  48. 48. ハックして何か新しい機能を!
  49. 49. 参考情報 49
  50. 50. GitHub Repository https://github.com/domaframework/doma
  51. 51. Doma 2 Java 8 時代のDBアクセス https://nakamura-to.github.io/presentation-doma2- with-java8/
  52. 52. Thank you

×