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

Post on 02-Jul-2015

3.781 views 12 download

description

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

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

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

GitHub Repository

https://github.com/domaframework/doma

Doma 2 Java 8 時代のDBアクセス

https://nakamura-to.github.io/presentation-doma2-with-java8/

Thank you