概要
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ファイル
ファイル名の形式
ファイル名は、次の形式でなければいけません。
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コメント
Domaでは、SQLコメント中に式を記述することで、値のバインディングや条件分岐を行います。 式を含みDomaに解釈されるSQLコメントを式コメントと呼びます。
バインド変数コメント
バインド変数を示す式コメントをバインド変数コメントと呼びます。
バインド変数は、java.sql.PreparedStatement
を介してSQLに設定されます。
バインド変数は/*~*/というブロックコメントで囲んで示します。 バインド変数の型は、基本型もしくはドメインクラスでなければいけません。 バインド変数コメントの直後にはテスト用データを指定する必要があります。 テスト用データは、実行時には使用されません。
単一のパラメータ
Daoインタフェースのメソッドのパラメータが基本型もしくはドメインクラスの場合、 このパラメータは、1つのバインド変数を表現できます。 バインド変数コメントはバインド変数を埋め込みたい場所に記述し、バインド変数コメントの直後にはテスト用データを指定しなければいけません。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectById(Integer employeeId);
select * from employee where employee_id = /* employeeId */99
Listのパラメータ
Daoインタフェースのメソッドのパラメータがjava.util.Listの場合、 このパラメータは、IN句内の複数のバインド変数を表現できます。 バインド変数コメントはINキーワードの直後に置き、バインド変数コメントの直後には括弧つきでテスト用データを指定しなければいけません。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectByIdList(List<Integer> employeeIdList);
select * from employee where employee_id in /* employeeIdList */(1,2,3)
エンティティクラスのパラメータ
Daoインタフェースのメソッドのパラメータがエンティティクラスの場合、 エンティティクラスのパラメータは、複数のバインド変数コメントに対応します。 バインド変数コメントの中では、ドット(.)を使用しエンティティのフィールドにアクセスできます。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
List<Employee> selectByNameAndSalary(Employee employee);
select * from employee where employee_name = /* employee.employeeName */'abc' and salary = /* employee.salary */1234
フィールドにアクセスする代わりに、publicなメソッドを呼び出すことも可能です。
select * from employee where salary = /* employee.getTaxedSalary() */1234
埋め込み変数コメント
埋め込み変数を示す式コメントを埋め込み変数コメントと呼びます。 埋め込み変数の値は、SQLを組み立てる際にSQLの一部として直接埋め込まれます。 SQLインジェクションを防ぐため、埋め込み変数の一部としてシングルクォテーション、セミコロン、行コメント、ブロックコメントは含めることは禁止しています。
埋め込み変数は/*#~*/というブロックコメントで示します。埋め込み変数の名前はメソッドのパラメータ名にマッピングされます。 埋め込み変数はORDER BY句など、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
条件コメント
if
と end
条件分岐を示す式コメントを条件コメントと呼びます。
構文は、/*%if 条件式*/ ~ /*%end*/
となります。
条件式は、結果がbooleanもしくはjava.lang.Boolean型と評価される式でなければいけません。
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*/
繰り返しコメント
for
と end
繰り返しを示す式コメントを繰り返しコメントと呼びます。
構文は、/*%for 識別子 : 式*/ ~ /*%end*/
となります。
識別子は、繰り返される要素を指す変数です。
式は、java.lang.Iterable型として評価される式でなければいけません。
select * from employee where /*%for name : names */ employee_name like /* name */'hoge'/*%hasNext " or " */ /*%end*/
このSQL文は、names
が3つの要素からなるリストを表す場合、次のような準備された文に変換されます。
select * from employee where employee_name like ? or employee_name like ? or employee_name like ?
hasNext
/*%for 識別子 : 式*/
から /*%end*/
までの内側の/*%forhasNext 式*/
は、
次の繰り返しが続く場合にのみ、式の評価結果を繰り返される文字列に連結することを示します。
式は、String、Character、charのいずれかの型として評価可能な式でなければいけません。
通常のブロックコメント
/*
の直後に続く3文字目が次のような値の場合、それは通常のブロックコメントだとみなされます。
- Javaの識別子の先頭で使用できない文字(ただし、空白と式で特別意味をもつ「
%
」、「#
」、「@
」、「"
」、「'
」は除く)
たとえば、次の例はすべて通常のブロックコメントとみなされます。
/**~*/ /*+~*/ /*=~*/ /*:~*/ /*;~*/ /*(~*/ /*)~*/ /*&~*/
一方、次の例はすべて式コメントだとみなされます。
/* ~*/ ...--3文字目が空白であるため式コメントです。 /*a~*/ ...--3文字目がJavaの識別子の先頭で使用可能な文字であるため式コメントです。 /*$~*/ ...--3文字目がJavaの識別子の先頭で使用可能な文字であるため式コメントです。 /*%~*/ ...--3文字目が条件コメントを表す「%
」であるため式コメントです。 /*#~*/ ...--3文字目が埋め込み変数コメントを表す「#
」であるため式コメントです。 /*@~*/ ...--3文字目が組み込み関数もしくはクラス名を表す「@
」であるため式コメントです。
式言語
式コメントには式を記述できます。 文法は、Javaとほとんど同じです。 ただし、Javaで可能なことすべてができるわけではありません。
リテラル
以下のリテラルが用意されています。
リテラル | 型 |
---|---|
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 |
数値の型は、リテラルの最後に「L」や「F」などを付与して区別します。 「L」や「F」などは大文字でなければいけません。
select * from employee where /*%if employeeName != null && employeeName.length() > 10 */ employee_name = /*employeeName*/'smith' /*%end*/
比較演算子
以下の比較演算子を使用できます。
比較演算子 |
---|
== |
!= |
< |
<= |
> |
>= |
比較演算子を利用するには、 被演算子が java.lang.Comparable
を実装している必要があります。
<
、<=
、>
、>=
では、
非演算子にnull
リテラルを使用できません。
select * from employee where /*%if employeeName.indexOf("s") > -1 */ employee_name = /* employeeName */'smith' /*%end*/
論理演算子
以下の論理演算子を使用できます。
論理演算子 |
---|
! |
&& |
|| |
括弧を使って、演算子が適用される優先度を制御できます。
select * from employee where /*%if (departmentId == null || managerId == null) and employee_name != null */ employee_name = /* employeeName */'smith' /*%end*/
算術演算子
以下の算術演算子を使用できます。
算術演算子 |
---|
+ |
- |
* |
/ |
被演算子は数値型でなければいけません。
select * from employee where salary = /* salary + 1000 */0
その他の演算子
+
演算子を使って文字を連結できます。
被演算子は次のいずれかの型でなければいけません。
- java.lang.String
- java.lang.Character
- char
select * from employee where employee_name like /* employeeName + "_" */'smith'
インスタンスメソッドの呼び出し
ドット(.)で区切ってメソッド名を指定することでインスタンスメソッドを実行可能です。
実行可能なメソッドは、可視性が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*/
インスタンスフィールドへのアクセス
ドット(.)で区切ってフィールド名を指定することでインスタンスフィールドにアクセスできます。 可視性はprivateであってもアクセス可能です。
select * from employee where employee_name = /* employee.employeeName */'smith'
staticメソッドの呼び出し
@
で囲まれたクラスの完全修飾名にメソッドを続けることでstaticメソッドを実行可能です。
実行可能なメソッドは、可視性がpublic
なものだけに限られます。
select * from employee where /*%if @java.util.regex.Pattern@matches("^[a-z]*$", employeeName) */ employee_name = /* employeeName */'smith' /*%end*/
staticフィールドへのアクセス
@
で囲まれたクラスの完全修飾名にフィールドを続けることでstaticフィールドにアクセスできます。
可視性はprivateであってもアクセス可能です。
select * from employee where /*%if employeeName.lenght() < @java.lang.Byte@MAX_VALUE */ employee_name = /* employeeName */'smith' /*%end*/
組み込み関数の使用
組み込み関数は、主に、SQLにバインドする前にバインド変数の値を変更するためのユーティリティです。
たとえば、likeで前方一致検索を行う場合に、次のように記述できます。
select * from employee where employee_name like /* @prefix(employee.employeeName) */'smith'
ここでは、@prefix(employee.employeeName)
というように、
employee.employeeName
を @prefix
関数に渡しています。
組み込み関数の名前はすべて@
で始まります。
employee.employeeName
の値が「ABC」である場合、
SQLにバインディングされる値は「ABC%」となります。
もし、employee.employeeName
の値が「AB%C」というように「%」を含んでいる場合、
「%」はデフォルトのエスケープシーケンスでエスケープされ、SQLにバインディングされる値は「AB\%C%」となります。
使用可能な関数は以下のとおりです。デフォルトでは、いずれの関数も最初の引数にnull
を渡した場合、null
を返します。
戻り値の型 | 関数名とパラメータ | 概要 |
---|---|---|
String |
@prefix(String prefix) |
前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープにはデフォルトのエスケープ文字を用いて行われます。 |
String |
@prefix(String prefix, char escape) |
前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。 |
String |
@suffix(String suffix) |
後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープはデフォルトのエスケープ文字を用いて行われます。 |
String |
@suffix(String suffix, char escape) |
後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。 |
String |
@contain(String inside) |
中間一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前と後ろに付与した文字列です。エスケープはデフォルトのエスケープ文字を用いて行われます。 |
String |
@contain(String inside, char escape) |
中間一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前と後ろに付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。 |
java.sql.Date |
@roundDownTimePart(java.sql.Date date) |
時刻部分を切り捨てることを示します。戻り値は時刻部分が切り捨てられた新しい日付です。 |
java.sql.Timestamp |
@roundDownTimePart(java.sql.Timestamp timestamp) |
時刻部分を切り捨てることを示します。戻り値は時刻部分が切り捨てられた新しいタイムスタンプです。 |
java.sql.Date |
@roundUpTimePart(java.sql.Date date) |
時刻部分を切り上げることを示します。戻り値は時刻部分が切り上げられた新しい日付です。 |
java.sql.Timestamp |
@roundUpTimePart(java.sql.Timestamp timestamp) |
時刻部分を切り上げることを示します。戻り値は時刻部分が切り上げられた新しいタイムスタンプです。 |
これらの関数は、org.seasar.doma.expr.ExpressionFunctions
のメソッドに対応しています。