概要
SQLファイルは、SQL文を格納したテキストファイルで、Daoのメソッドにマッピングされます。 SQLのブロックコメント(/* */)や行コメント(--)を使用することで、バインド変数や動的なSQLのための条件分岐を表現できます。 SQLのツールでそのままそのSQLを実行できるように、バインド変数にはテスト用のデータを指定します。テスト用のデータは、実行時には使用されません。 たとえば、SQLファイルには次のようなSQL文が格納されます。
select * from employee where employee_id = /*employeeId*/99
ここでは、ブロックコメントで囲まれた employeeId
がDaoインタフェースのメソッドのパラメータに対応し、
直後の 99
はテスト用の条件になります。
対応するDaoインタフェースのメソッドは次のとおりです。
Employee selectById(employeeId);
SQLファイルにマッピングするためのアノテーション
SQLファイルとDaoのメソッドのマッピングは次のアノテーションで示します。
@Select
@Insert(sqlFile = true)
@Update(sqlFile = true)
@Delete(sqlFile = true)
@BatchInsert(sqlFile = true)
@BatchUpdate(sqlFile = true)
@BatchDelete(sqlFile = true)
SQLファイル
ファイル名の形式
ファイル名は、次の形式でなければいけません。
META-INF/Dalのクラスの完全修飾名をディレクトリに変換したもの/Data Access Objectのメソッド名.sql
例えば、 Daoのクラスが aaa.bbb.EmployeeDao
で
マッピングしたいメソッドが selectById
の場合、パス名は次のようになります。
META-INF/aaa/bbb/EmployeeDao/selectById.sql
複数のRDBMSに対応する必要があり特定のRDBMSでは別のSQLファイルを使いたい場合、
.sql
の前にハイフン区切りでRDBMS名を入れることで、優先的に使用するファイルを指示できます。
たとえば、PostgreSQL専用のSQLファイルは次の名前にします。
META-INF/aaa/bbb/EmployeeDao/selectById-postgres.sql
この場合、PostgreSQLを使用している場合に限り、META-INF/aaa/bbb/EmployeeDao/selectById.sql
よりも
META-INF/aaa/bbb/EmployeeDao/selectById-postgres.sql
が優先的に使用されます。
RDBMS名は、 org.seasar.doma.jdbc.dialect.Dialect
の getName
メソッドの値が使用されます。
SQLコメント
バインド変数コメント
バインド変数は/*~*/というブロックコメントで囲んで示します。バインド変数の名前はDaoインタフェースのメソッドのパラメータ名にマッピングされます。 ブロックコメント直後にはテスト用データを指定する必要があります。テスト用データは、実行時には使用されません。
単一のパラメータ
Daoインタフェースのメソッドのパラメータが基本型もしくはドメインクラスの場合について説明します。 単一のパラメータは、1つのバインド変数コメントに対応します。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectById(Integer employeeId);
select * from employee where employee_id = /*employeeId*/99
/*employeeId*/
は、/* employeeId */
のように空白を含めて記述することも可能です。
Listのパラメータ
Daoインタフェースのメソッドのパラメータが基本型もしくはドメインクラスを要素とするjava.util.Listの場合について説明します。 Listのパラメータは、IN句の1つのバインド変数コメントに対応しますが、複数の値をバインドできます。 ブロックコメントはINキーワードの直後に置き、ブロックコメントの直後には括弧つきでテスト用データを指定します。テスト用データは、実行時には使用されません。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectByIdList(List<Integer> employeeIdList);
select * from employee where employee_id in /*employeeIdList*/(1,2,3)
/*employeeIdList*/
は、/* employeeIdList */
のように空白を含めて記述することも可能です。
エンティティクラスのパラメータ
Daoインタフェースのメソッドのパラメータがエンティティクラスの場合について説明します。 エンティティクラスのパラメータは、複数のバインド変数コメントに対応します。 ブロックコメントの中では、ドット(.)を使用しエンティティのフィールドにアクセスできます。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectByNameAndSalary(Employee employee);
select * from employee where employee_name = /*employee.employeeName*/'abc' and salary = /*employee.salary*/1234
/*employee.employeeName*/
は、/* employee.employeeName */
のように空白を含めて記述することも可能です。
フィールドにアクセスする代わりに、publicなメソッドを呼び出すことも可能です。
select * from employee where salary = /*employee.getTaxedSalary()*/1234
埋め込み変数コメント
埋め込み変数は/*#~*/というブロックコメントで示します。埋め込み変数の名前はメソッドのパラメータ名にマッピングされます。 埋め込み変数はORDER BY句など、SQLの一部をプログラムで組み立てたい場合に使用できます。 安全のため、埋め込み変数の一部にシングルクォテーション、セミコロン、ラインコメント、ブロックコメントを含めることは認められていません。 シングルクォテーション以外のSQLインジェクション対策はアプリケーションの責任になります。 Daoのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectAll(BigDecimal salary, String orderyBy);
select * from employee where salary > /*salary*/100 /*#orderBy*/
Daoの呼び出し例は次の通りです。
EmployeeDao dao = new EmployeeDaoImpl(); BigDecimal salary = new BigDecimal(1000); String orderBy = "order by salary asc, employee_name"; List<Employee> list = dao.selectAll(salary, orderBy);
発行されるSQLは次のようになります。
select * from employee where salary > ? order by salary asc, employee_name
/*#orderBy*/
は、/*# orderBy */
のように空白を含めて記述することも可能です。
条件コメント
if
と end
条件によってSQLを組み立てる場合は、/*%if 条件*/ ~ /*%end*/
という構文を使用できます。
select * from employee where /*%if employeeId != null*/employee_id = /*employeeId*/99/*%end*/
このSQL文は、 employeeId
が null
でない場合 次のような準備された文に変換されます。
select * from employee where employee_id = ?
このSQL文は、 employeeId
null
の場合に次のような準備された文に変換されます。
select * from employee
WHERE
や HAVING
の自動除去
条件コメントを使用した場合、条件の前にあるWHEREやHAVINGについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでemployeeId
が null
の場合、
select * from employee where /*%if employeeId != null*/ employee_id = /*employeeId*/99 /*%end*/
/*%if*/
の前の where
は自動で除去されます。
select * from employee
AND
や OR
の自動除去
条件コメントを使用した場合、条件の後ろにつづくANDやORについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでemployeeId
が null
の場合、
select * from employee where /*%if employeeId != null*/ employee_id = /*employeeId*/99 /*%end*/ and employeeName like 's%'
/*%end*/
の後ろの and
は自動で除去されます。
select * from employee where employeeName like 's%'
elseif
と else
/*%if 条件*/
と /*%end*/
の間では、
行コメントを使用した --elseif 条件--
や --else
という構文も使用できます。
select * from employee where /*%if employeeId != null*/ employee_id = /*employeeId*/99 --elseif employeeId == 999-- department_id is null --else employee_id is null /*%end*/
ネストした条件コメント
条件コメントはネストさせることができます。
select * from employee where /*%if employeeId != null*/ employee_id = /*employeeId*/99 /*%if employeeName != null*/ and employee_name = /*employeeName*/'hoge' /*%end*/ /*%end*/
通常のブロックコメント
/*
の直後に、空白以外のJavaの変数で使用できない値が続く場合、それは通常のSQLコメントだとみなされます。
ただし、条件コメントを示す %
や
埋め込みコメントを示す#
がつづく場合は通常のコメントとみなされません。
たとえば、次の例は通常のブロックコメントとみなされます。
/** aaa */
/*+aaa */
次の例は通常のブロックコメントとみなされません。
/* id */
/*$aaa*/
/*%aaa*/
/*#aaa*/
/* id */
は空白が除去されid
というバインド変数とみなされます。
/*$aaa*/
は$aaa
というバインド変数とみなされます。
/*%aaa*/
は%
で始まっているため条件コメントとして解釈されます(しかし、if
でもend
でもないためエラーとなります)。
/*#aaa*/
はaaa
という埋め込み変数コメントとみなされます。
式言語
条件コメントの /*%if 条件*/
や、 --elseif 条件--
の条件部には、式を記述できます。
定数
次の定数が用意されています。
定数 | 型 |
null |
void |
true |
boolean |
false |
boolean |
10 |
int |
10L |
long |
0.123F |
float |
0.123D |
double |
0.123B |
java.math.BigDecimal |
'a' |
char |
"a" |
String |
比較演算子
以下の比較演算子を使用できます。
比較演算子 |
== |
!= |
< |
<= |
> |
>= |
比較演算子を利用するには、 被演算子が java.lang.Comparable
を実装している必要があります。
<
、<=
、>
、>=
では、
非演算子にnull
リテラルを使用できません。
論理演算子
以下の論理演算子を使用できます。
論理演算子 |
! |
&& |
|| |
括弧を使って、演算子が適用される優先度を制御できます。
select * from employee where /*%if (departmentId == null || managerId == null) and employee_name != null*/ employee_name = /*employeeName*/'smith' /*%end*/
メソッド呼び出し
ドット(.)で区切ってメソッド名を指定することでメソッドを実行可能です。実行可能なメソッドは、可視性がpublic
なものだけに限られます。
select * from employee where /*%if employeeName.startsWith("s") */ employee_name = /*employeeName*/'smith' /*%end*/
引数がない場合は、メソッド名の後ろに()を指定します。
select * from employee where /*%if employeeName.length() > 10 */ employee_name = /*employeeName*/'smith' /*%end*/