概要
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インタフェースのメソッドのパラメータ名にマッピングされます。 バインド変数コメントの直後にはテスト用データを指定する必要があります。 テスト用データは、実行時には使用されません。
単一のパラメータ
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
埋め込み変数コメント
埋め込み変数を示す式コメントを埋め込み変数コメントと呼びます。 埋め込み変数の値は、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
/*#orderBy*/
は、/*# orderBy */
のように空白を含めて記述することも可能です。
条件コメント
if
と end
条件分岐を示す式コメントを条件コメントと呼びます。
構文は、/*%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*/
通常のブロックコメント
/*
の直後に続く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 /*@startWith(employee.employeeName)*/'smith'
ここでは、@startWith(employee.employeeName)
というように、
employee.employeeName
を @startWith
関数に渡しています。
組み込み関数の名前はすべて@
で始まります。
employee.employeeName
の値が「ABC」である場合、
SQLにバインディングされる値は「ABC%」となります。
もし、employee.employeeName
の値が「AB%C」というように「%」を含んでいる場合、
「%」はデフォルトのエスケープシーケンスでエスケープされ、SQLにバインディングされる値は「AB\%C%」となります。
使用可能な関数は以下のとおりです。デフォルトでは、いずれの関数も最初の引数にnull
を渡した場合、null
を返します。
戻り値の型 | 関数名とパラメータ | 概要 |
---|---|---|
String |
@startWith(String text) |
前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープにはデフォルトのエスケープ文字を用いて行われます。 |
String |
@startWith(String text, char escape) |
前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。 |
String |
@endWith(String text) |
後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープはデフォルトのエスケープ文字を用いて行われます。 |
String |
@endWith(String text, char escape) |
後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。 |
String |
@contain(String text) |
前方後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前と後ろに付与した文字列です。エスケープはデフォルトのエスケープ文字を用いて行われます。 |
String |
@contain(String text, 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
のメソッドに対応しています。