SQLファイルは、SQL文を格納したテキストファイルで、Daoのメソッドにマッピングされます。 SQLのブロックコメント(/* */)や行コメント(--)を使用することで、バインド変数や動的なSQLのための条件分岐を表現できます。 SQLのツールでそのままそのSQLを実行できるように、バインド変数にはテスト用のデータを指定します。テスト用のデータは、実行時には使用されません。 たとえば、SQLファイルには次のようなSQL文が格納されます。
ここでは、ブロックコメントで囲まれた employeeId
がDaoインタフェースのメソッドのパラメータに対応し、
直後の 99
はテスト用の条件になります。
対応するDaoインタフェースのメソッドは次のとおりです。
SQLファイルとDaoのメソッドのマッピングは次のアノテーションで示します。
SQLファイルはクラスパスが通った META-INF
ディレクトリ以下に配置しなければいけません。
ファイル名は、次の形式でなければいけません。
例えば、 Daoのクラスが aaa.bbb.EmployeeDao
で
マッピングしたいメソッドが selectById
の場合、パス名は次のようになります。
複数のRDBMSに対応する必要があり特定のRDBMSでは別のSQLファイルを使いたい場合、
.sql
の前にハイフン区切りでRDBMS名を入れることで、優先的に使用するファイルを指示できます。
たとえば、PostgreSQL専用の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
メソッドの値が使用されます。
あらかじめ用意されたDialectについてそれぞれのRDBMS名を以下の表に示します。
データベース | 方言クラスの名前 | RDBMS名 |
---|---|---|
DB2 | org.seasar.doma.jdbc.dialect.Db2Dialect |
db2 |
H2 Database Engine 1.2.126 | org.seasar.doma.jdbc.dialect.H212126Dialect |
h2 |
H2 Database Engine | org.seasar.doma.jdbc.dialect.H2Dialect |
h2 |
HSQLDB | org.seasar.doma.jdbc.dialect.HsqldbDialect |
hsqldb |
Microsoft SQL Server 2008 | org.seasar.doma.jdbc.dialect.Mssql2008Dialect |
mssql2008 |
Microsoft SQL Server | org.seasar.doma.jdbc.dialect.MssqlDialect |
mssql |
MySQL | org.seasar.doma.jdbc.dialect.MySqlDialect |
mysql |
Oracle Database | org.seasar.doma.jdbc.dialect.OracleDialect |
oracle |
PostgreSQL | org.seasar.doma.jdbc.dialect.PostgresDialect |
postgres |
SQLite | org.seasar.doma.jdbc.dialect.SqliteDialect |
sqlite |
SQLファイルのエンコーディングはUTF-8でなければいけません。
Domaでは、SQLコメント中に式を記述することで、値のバインディングや条件分岐を行います。 Domaに解釈されるSQLコメントを式コメントと呼びます。
バインド変数を示す式コメントをバインド変数コメントと呼びます。
バインド変数は、java.sql.PreparedStatement
を介してSQLに設定されます。
バインド変数は/*~*/というブロックコメントで囲んで示します。 バインド変数の名前は、Daoメソッドのパラメータ名に対応します。 対応するパラメータの型は、基本型もしくはドメインクラスでなければいけません。 バインド変数コメントの直後にはテスト用データを指定する必要があります。 テスト用データは、実行時には使用されません。
Daoインタフェースのメソッドのパラメータが基本型もしくはドメインクラスの場合、 このパラメータは、1つのバインド変数を表現できます。 バインド変数コメントはバインド変数を埋め込みたい場所に記述し、バインド変数コメントの直後にはテスト用データを指定しなければいけません。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
Iterable
型のパラメータ
Daoインタフェースのメソッドのパラメータがjava.lang.Iterable
のサブタイプの場合、
このパラメータは、IN句内の複数のバインド変数を表現できます。
ただし、java.lang.Iterable
のサブタイプの実型引数は基本型もしくはドメインクラスでなければいけません。
バインド変数コメントはINキーワードの直後に置き、バインド変数コメントの直後には括弧つきでテスト用データを指定しなければいけません。
Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。
Daoインタフェースのメソッドのパラメータが基本型もしくはドメインクラスでない場合、 パラメータは、複数のバインド変数コメントに対応します。 バインド変数コメントの中では、ドット(.)を使用し任意の型のフィールドやメソッドにアクセスできます。 Daoインタフェースのメソッドと、対応するSQLの例は次のとおりです。 EmployeeDtoクラスには、employeeNameフィールドやsalaryフィールドが存在するものとします。
フィールドにアクセスする代わりに、publicなメソッドを呼び出すことも可能です。
埋め込み変数を示す式コメントを埋め込み変数コメントと呼びます。 埋め込み変数の値は、SQLを組み立てる際にSQLの一部として直接埋め込まれます。 SQLインジェクションを防ぐため、埋め込み変数の値にシングルクォテーション、セミコロン、行コメント、ブロックコメントは含めることを禁止しています。
埋め込み変数は/*#~*/というブロックコメントで示します。埋め込み変数の名前はDaoメソッドのパラメータ名にマッピングされます。 埋め込み変数はORDER BY句など、SQLの一部をプログラムで組み立てたい場合に使用できます。 Daoのメソッドと、対応するSQLの例は次のとおりです。
Daoの呼び出し例は次の通りです。
発行されるSQLは次のようになります。
条件分岐を示す式コメントを条件コメントと呼びます。 構文は、次のとおりです。 条件式は、結果がbooleanもしくはjava.lang.Boolean型と評価される式でなければいけません。 例を示します。
上記のSQL文は、 employeeId
が null
でない場合 次のような準備された文に変換されます。
このSQL文は、 employeeId
がnull
の場合に次のような準備された文に変換されます。
ifの条件が成り立たない場合にifの外にあるWHERE句が出力されないのは、WHERE
や HAVING
の自動除去機能が働いているためです。
条件コメントを使用した場合、条件の前にあるWHEREやHAVINGについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでemployeeId
が null
の場合、
/*%if ~*/
の前の where
は自動で除去され、次のSQLが生成されます。
条件コメントを使用した場合、条件の後ろにつづくANDやORについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでemployeeId
が null
の場合、
/*%end*/
の後ろの and
は自動で除去され、次のSQLが生成されます。
/*%if 条件式*/
と /*%end*/
の間では、
elseifやelseを表す次の構文も使用できます。
例を示します。
上のSQLは、employeeId != null が成立するとき実際は次のSQLに変換されます。
employeeId == null department_id != null が成立するとき、実際は次のSQLに変換されます。 department_idの直前のANDは自動で除去されるため出力されません。
employeeId == null department_id == null が成立するとき、実際は次のSQLに変換されます。 department_idの直前のANDは自動で除去されるため出力されません。
過去との互換性のため、/*%if 条件式*/
と /*%end*/
の間では、
行コメントを使用した次の構文も使用できます。
特に理由がない限り、ブロックコメントの /*%elseif 条件式*/ や /*%else*/ を使用してください。
elseifやelseを行コメントで表した場合の例を示します。
条件コメントはネストさせることができます。
条件コメントのifとendはSQLの同じ節に含まれなければいけません。 節とは、SELECT節、FROM節、WHERE節、GROUP BY節、HAVING節、ORDER BY節などです。 次の例では、ifがFROM節にありendがWHERE節にあるため不正です。
また、ifとendは同じレベルの文に含まれなければいけません。 次の例では、ifが括弧の外にありendが括弧の内側にあるので不正です。
繰り返しを示す式コメントを繰り返しコメントと呼びます。 構文は、次のとおりです。 識別子は、繰り返される要素を指す変数です。 式は、java.lang.Iterable型として評価される式でなければいけません。
例を示します。
上記のSQL文は、names
が3つの要素からなるリストを表す場合、次のような準備された文に変換されます。
/*%for 識別子 : 式*/
から /*%end*/
までの内側では、次の2つの特別な変数を使用できます。
itemは識別子です。つまり、forの識別子が「name」の場合、この変数はそれぞれ「name_has_next」と「name_index」となります。
item_has_nextは、次の繰り返し要素が存在するかどうかを示すbooleanの値です。
item_indexは、繰り返しのindexを表すintの値です。値は0始まりです。
繰り返しコメントを使用した場合、コメントの前にあるWHEREやHAVINGについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでnames
のsizeが0の場合(繰り返しが行われない場合)、
/*%for ~*/
の前の where
は自動で除去され、次のSQLが生成されます。
繰り返しコメントを使用した場合、コメントの後ろにつづくANDやORについて、自動で出力の要/不要を判定します。
たとえば、次のようなSQLでnames
のsizeが0の場合(繰り返しが行われない場合)、
/*%end*/
の後ろの or
は自動で除去され、次のSQLが生成されます。
繰り返しコメントのforとendはSQLの同じ節に含まれなければいけません。 節とは、SELECT節、FROM節、WHERE節、GROUP BY節、HAVING節、ORDER BY節などです。
また、forとendは同じレベルの文に含まれなければいけません。 つまり、括弧の外でfor、括弧の内側でendという記述は認められません。
/*
の直後に続く3文字目が、Javaの識別子の先頭で使用できない文字(ただし、空白と式で特別な意味をもつ「%
」、「#
」、「@
」、「"
」、「'
」は除く)の場合、それは通常のブロックコメントだとみなされます。
たとえば、次の例はすべて通常のブロックコメントとみなされます。
一方、次の例はすべて式コメントだとみなされます。
特に理由がない場合、通常のブロックコメントには/**~*/
を使用するのがよいでしょう。
--
の直後に、elseif
やelse
がつづかない場合、それは通常の行コメントだとみなされます。
たとえば、次の例は通常の行コメントだとみなされます。
一方、次の例はすべて式コメントだとみなされます。
特に理由がない場合、通常の行コメントは使用しないか、---
を使用するのがいいでしょう。
式コメントには式を記述できます。 文法は、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」などは大文字でなければいけません。
以下の比較演算子を使用できます。
演算子 | 説明 |
---|---|
== | 等値演算子 |
!= | 不等演算子 |
< | 小なり演算子 |
<= | 小なりイコール演算子 |
> | 大なり演算子 |
>= | 大なりイコール演算子 |
比較演算子を利用するには、 被演算子が java.lang.Comparable
を実装している必要があります。
<
、<=
、>
、>=
では、
被演算子がnull
であってはいけません。
以下の論理演算子を使用できます。
演算子 | 説明 |
---|---|
! | 論理否定演算子 |
&& | 論理積演算子 |
|| | 論理和演算子 |
括弧を使って、演算子が適用される優先度を制御できます。
以下の算術演算子を使用できます。
演算子 | 説明 |
---|---|
+ | 加算演算子 |
- | 減算演算子 |
* | 乗算演算子 |
/ | 除算演算子 |
% | 剰余演算子 |
被演算子は数値型でなければいけません。
連結演算子(+
)を使って文字を連結できます。
被演算子は次のいずれかの型でなければいけません。
ドット(.)で区切ってメソッド名を指定することでインスタンスメソッドを実行可能です。
実行可能なメソッドは、可視性がpublic
なものだけに限られます。
引数がない場合は、メソッド名の後ろに()を指定します。
ドット(.)で区切ってフィールド名を指定することでインスタンスフィールドにアクセスできます。 可視性はprivateであってもアクセス可能です。
@
で囲まれたクラスの完全修飾名にメソッドを続けることでstaticメソッドを実行可能です。
実行可能なメソッドは、可視性がpublic
なものだけに限られます。
@
で囲まれたクラスの完全修飾名にフィールドを続けることでstaticフィールドにアクセスできます。
可視性はprivateであってもアクセス可能です。
組み込み関数は、主に、SQLにバインドする前にバインド変数の値を変更するためのユーティリティです。
たとえば、likeで前方一致検索を行う場合に、次のように記述できます。
ここでは、@prefix(employee.employeeName)
というように、
employee.employeeName
を @prefix
関数に渡しています。
@prefix
関数は、パラメータで受け取る文字列を前方一致検索用の文字列に変換します。また、特別な意味を持つ文字をエスケープします。
この例では、employee.employeeName
の値が「ABC」である場合、
値は「ABC%」に変換されます。
もし、employee.employeeName
の値が「AB%C」というように「%」を含んでいる場合、
「%」はデフォルトのエスケープシーケンス($)でエスケープされ、値は「AB$%C%」に変換されます。
使用可能な関数は以下のとおりです。
戻り値の型 | 関数名とパラメータ | 概要 |
---|---|---|
String | @escape(String text) | LIKE演算のためのエスケープを行うことを示します。戻り値は入力値をエスケープした文字列です。エスケープにはデフォルトのエスケープ文字($)を用いて行われます。引数にnull を渡した場合、null を返します。 |
String | @escape(String text, char escapeChar) | LIKE演算のためのエスケープを行うことを示します。戻り値は入力値をエスケープした文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。最初の引数にnull を渡した場合、null を返します。 |
String | @prefix(String prefix) | 前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープにはデフォルトのエスケープ文字($)を用いて行われます。引数にnull を渡した場合、null を返します。 |
String | @prefix(String prefix, char escapeChar) | 前方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを後ろに付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。最初の引数にnull を渡した場合、null を返します。 |
String | @suffix(String suffix) | 後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープはデフォルトのエスケープ文字($)を用いて行われます。引数にnull を渡した場合、null を返します。 |
String | @suffix(String suffix, char escapeChar) | 後方一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前に付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。最初の引数にnull を渡した場合、null を返します。 |
String | @infix(String infix) | 中間一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前と後ろに付与した文字列です。エスケープはデフォルトのエスケープ文字($)を用いて行われます。引数にnull を渡した場合、null を返します。 |
String | @infix(String infix, char escapeChar) | 中間一致検索を行うことを示します。戻り値は入力値をエスケープしワイルドカードを前と後ろに付与した文字列です。エスケープは第2引数で指定したエスケープ文字を用いて行われます。最初の引数にnull を渡した場合、null を返します。 |
String | @contain(String inside) | @infix(String infix) の別名です。非推奨です。 |
String | @contain(String inside, char escapeChar) | @infix(String infix, char escapeChar) の別名です。非推奨です。 |
java.util.Date | @roundDownTimePart(java.util.Date date) | 時刻部分を切り捨てることを示します。戻り値は時刻部分が切り捨てられた新しい日付です。引数にnull を渡した場合、null を返します。 |
java.sql.Date | @roundDownTimePart(java.sql.Date date) | 時刻部分を切り捨てることを示します。戻り値は時刻部分が切り捨てられた新しい日付です。引数にnull を渡した場合、null を返します。 |
java.sql.Timestamp | @roundDownTimePart(java.sql.Timestamp timestamp) | 時刻部分を切り捨てることを示します。戻り値は時刻部分が切り捨てられた新しいタイムスタンプです。引数にnull を渡した場合、null を返します。 |
java.util.Date | @roundUpTimePart(java.util.Date date) | 時刻部分を切り上げることを示します。戻り値は時刻部分が切り上げられた新しい日付です。引数にnull を渡した場合、null を返します。 |
java.sql.Date | @roundUpTimePart(java.sql.Date date) | 時刻部分を切り上げることを示します。戻り値は時刻部分が切り上げられた新しい日付です。引数にnull を渡した場合、null を返します。 |
java.sql.Timestamp | @roundUpTimePart(java.sql.Timestamp timestamp) | 時刻部分を切り上げることを示します。戻り値は時刻部分が切り上げられた新しいタイムスタンプです。引数にnull を渡した場合、null を返します。 |
boolean | @isEmpty(CharSequence charSequence) | 文字シーケンスがnull 、もしくは文字シーケンスの長さが0 の場合 true を返します。 |
boolean | @isNotEmpty(CharSequence charSequence) | 文字シーケンスがnull でない、かつ文字シーケンスの長さが0 でない場合 true を返します。 |
boolean | @isBlank(CharSequence charSequence) | 文字シーケンスがnull 、もしくは文字シーケンスの長さが0 、もしくは文字シーケンスが空白だけから形成される場合 true を返します。 |
boolean | @isNotBlank(CharSequence charSequence) | 文字シーケンスがnull でない、かつ文字シーケンスの長さが0 でない、かつ文字シーケンスが空白だけで形成されない場合 true を返します。 |
これらの関数は、org.seasar.doma.expr.ExpressionFunctions
のメソッドに対応しています。
関数を独自に定義し使用できます。
独自に定義した関数(カスタム関数)を使用するには次の設定が必要です。
org.seasar.doma.expr.ExpressionFunctions
を実装したクラスのメソッドとして定義する。
メソッドは、publicなインスタンスメソッドでなければいけない。
org.seasar.doma.expr.ExpressionFunctions
を受け取ることが可能)。
カスタム関数を呼び出すには、組み込み関数と同じように関数名の先頭に@をつけます。 たとえば、myfuncという関数の呼び出しは、次のように記述できます。