概要
検索は、すべて外部化されたSQLを使って行われます。
外部化されたSQLはすべてバインド変数(?)を使った準備された文に変換されて実行されますが、 このドキュメントでは、発行されたSQLがどのようなものかわかりやすくするためにバインド変数を実際の値に置き換えたログ用のSQLを示します。
単純な検索
src/test/java/tutorial/SelectTest.javaのtestSimpleSelectメソッドを参照してください。 引数をSQLファイルにバインディングして実行する単純な検索です。 バインディングにはバインド変数コメントを使用します。
使用するDaoのメソッド定義は次のとおりです。
@Select Employee selectById(Integer id);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectById.sqlです。SQLは次のように記述されています。
select * from employee where id = /* id */0
Daoのメソッドは次のように呼び出します。
Employee employee = dao.selectById(1);
SQLのログは次のように出力されます。
select * from employee where id = 1
条件分岐を用いた検索
src/test/java/tutorial/SelectTest.javaのtestConditinalSelectメソッドを参照してください。 SQLファイル中に条件コメントを記述することで動的なSQLを発行できます。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByAgeRange(Integer min, Integer max);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByAgeRange.sqlです。SQLは次のように記述されています。
select * from employee where /*%if min != null */ age >= /* min */10 /*%end */ /*%if max != null */ and age <= /* max */70 /*%end */ order by age
条件1
1番目のパラメータに30、2番目のパラメータに40を渡してDaoのメソッドを呼び出します。
List<Employee> list = dao.selectByAgeRange(30, 40);
SQLのログは次のように出力されます。
select * from employee where age >= 30 and age <= 40 order by age
条件2
1番目のパラメータに30、2番目のパラメータにnullを渡してDaoのメソッドを呼び出します。
list = dao.selectByAgeRange(30, null);
SQLのログは次のように出力されます。
select * from employee where age >= 30 order by age
条件分岐でelseを用いた検索
src/test/java/tutorial/SelectTest.javaのtestConditinalSelect2メソッドを参照してください。 SQLファイル中に条件コメントを記述することで動的なSQLを発行できますが、条件が成り立たない場合を /*%else*/ という式コメントで記述できます。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByName(String name);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByName.sqlです。SQLは次のように記述されています。
select * from employee where /*%if name != null*/ name = /*name*/'hoge' /*%else */ and name is null /*%end */
条件1
パラメータに"SMITH"を渡してDaoのメソッドを呼び出します。 この場合、name != null という条件が成り立ちます。
List<Employee> list = dao.selectByName("SMITH");
SQLのログは次のように出力されます。
select * from employee where name = 'SMITH'
条件2
パラメータにnullを渡してDaoのメソッドを呼び出します。 この場合、name != null という条件は成り立ちません。
list = dao.selectByName(null);
SQLのログは次のように出力されます。where句の直後のandは自動で除去されます。
select * from employee where name is null
elseだけではなくelseifを表すこともできます。 詳細はelseifとelseを参照してください。
繰り返しを用いた検索
src/test/java/tutorial/SelectTest.javaのtestLoopSelectメソッドを参照してください。 SQLファイル中に繰り返しコメントを記述することで、動的なSQLを発行できます。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByAges(List<Integer> ages);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByAges.sqlです。SQLは次のように記述されています。 次の要素がある場合にのみ表示したい文字列(この例ではor)は、埋め込み変数コメントで表します。
select * from employee where /*%for age : ages */ age = /* age */30 /*%if age_has_next */ /*# "or" */ /*%end */ /*%end */
Daoのメソッドは次のように呼び出します。
List<Integer> ages = Arrays.asList(30, 40, 50, 60); List<Employee> list = dao.selectByAges(ages);
SQLのログは次のように出力されます。
select * from employee where age = 30 or age = 40 or age = 50 or age = 60;
IN述語による検索
src/test/java/tutorial/SelectTest.javaのtestInPredicateメソッドを参照してください。 IN述語を使った検索です。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByNames(List<String> names);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByNames.sqlです。SQLは次のように記述されています。
select * from employee where name in /* names */('aaa', 'bbb')'
Daoのメソッドは次のように呼び出します。
List<String> names = Arrays.asList("JONES", "SCOTT", "XXX"); List<Employee> list = dao.selectByNames(names);
SQLのログは次のように出力されます。
select * from employee where name in ('JONES', 'SCOTT', 'XXX')
LIKE述語による検索
前方一致検索
src/test/java/tutorial/SelectTest.javaのtestLikePredicate_prefixメソッドを参照してください。 SQLファイル中で組み込み関数「@prefix()」を使用した前方一致検索ができます。 組み込み関数「@prefix()」を使用するとJavaのコード内で「%」を文字列に連結する処理が不要になります。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByNameWithPrefixMatching(String prefix);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByNameWithPrefixMatching.sqlです。SQLは次のように記述されています。
select * from employee where name like /* @prefix(prefix) */'X%' escape '$'
Daoのメソッドは次のように呼び出します。
List<Employee> list = dao.selectByNameWithPrefixMatching("S");
SQLのログは次のように出力されます。
select * from employee where name like 'S%' escape '$'
後方一致検索
src/test/java/tutorial/SelectTest.javaのtestLikePredicate_suffixメソッドを参照してください。 SQLファイル中で組み込み関数「@suffix()」を使用した後方一致検索ができます。 組み込み関数「@suffix()」を使用するとJavaのコード内で「%」を文字列に連結する処理が不要になります。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByNameWithSuffixMatching(String suffix);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByNameWithSuffixMatching.sqlです。SQLは次のように記述されています。
select * from employee where name like /* @suffix(suffix) */'%X' escape '$'
Daoのメソッドは次のように呼び出します。
List<Employee> list = dao.selectByNameWithSuffixMatching("S");
SQLのログは次のように出力されます。
select * from employee where name like '%S' escape '$'
中間一致検索
src/test/java/tutorial/SelectTest.javaのtestLikePredicate_insideメソッドを参照してください。 SQLファイル中で組み込み関数「@contain()」を使用した中間一致検索ができます。 組み込み関数「@contain()」を使用するとJavaのコード内で「%」を文字列に連結する処理が不要になります。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByNameWithInsideMatching(String inside);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByNameWithInsideMatching.sqlです。SQLは次のように記述されています。
select * from employee where name like /* @contain(inside) */'%X%' escape '$'
Daoのメソッドは次のように呼び出します。
List<Employee> list = dao.selectByNameWithInsideMatching("A");
SQLのログは次のように出力されます。
select * from employee where name like '%A%' escape '$'
日付による範囲検索
src/test/java/tutorial/SelectTest.javaのtestSelectByTimestampRangeメソッドを参照してください。 日付を用いて範囲検索を行う場合に、日付の時刻部分を切り捨てたり、日付を一日後ろへずらして切りのいい値にしたいことがあります。 そのような場合には、組み込み関数の「@roundDownTimePart()」と「@roundUpTimePart()」を使用します。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByHiredateRange(Timestamp from, Timestamp to);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByHiredateRange.sqlです。SQLは次のように記述されています。
select * from employee where hiredate >= /* @roundDownTimePart(from) */'2001-01-01 12:34:56' and hiredate < /* @roundUpTimePart(to) */'2001-01-01 12:34:56'
Daoのメソッドは次のように呼び出します。
Timestamp from = Timestamp.valueOf("2008-01-20 12:34:56"); Timestamp to = Timestamp.valueOf("2008-03-20 12:34:56"); List<Employee> list = dao.selectByHiredateRange(from, to);
SQLのログは次のように出力されます。
select * from employee where hiredate >= '2008-01-20 00:00:00.0' and hiredate < '2008-03-21 00:00:00.0'
Javaコードからの引数と比べると時刻部分の切り捨てや切り上げが行われていることがわかります。
文字シーケンスを組み込み関数で判定する検索
src/test/java/tutorial/SelectTest.javaのtestIsNotEmptyFunctionメソッドを参照してください。
文字シーケンスがnullかどうか、長さが0かどうかといった条件を基に条件分岐したい場合があります。たとえば、「nullでなくかつ長さが0でない場合にのみ検索条件に含める」には、次のようなSQLを記述できます。
select * from employee where /*%if name != null && name.length != 0 */ name = /* name */'hoge' /*%end*/
しかし、上記のように書くのは冗長です。これを簡潔に記述するために、組み込み関数の「@isNotEmpty」が使用できます。 @isNotEmptyを使用すると次のように記述できます。 これは上記のSQLと意味的にはまったく同等です。
select * from employee where /*%if @isNotEmpty(name) */ name = /* name */'hoge' /*%end*/
使用するDaoのメソッド定義は次のとおりです。 メソッドの定義は、@isNotEmptyの使用の有無に影響されません。
@Select List<Employee> selectByNotEmptyName(String name);
Daoのメソッドは次のように呼び出します。
List<Employee> list = dao.selectByNotEmptyName("SMITH");
SQLのログは次のように出力されます。
select * from employee where name = 'SMITH'
引数にnullや空文字を渡した場合、実行されるSQLは変化します。
list = dao.selectByNotEmptyName(null);
list = dao.selectByNotEmptyName("");
引数にnullや空文字を渡した場合、SQLのログは次のように出力されます。
select * from employee
文字シーケンスを扱う組み込み関数には、「@isNotEmpty」の他に、「@isEmpty」、「@isBlank」、「@isNotBlank」があります。 それぞれの説明については組み込み関数を参照してください。
ドメインによる検索
src/test/java/tutorial/SelectTest.javaのtestSelectByDomainメソッドを参照してください。 ドメインを条件に使用した検索です。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectBySalary(Salary salary);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectBySalary.sqlです。 SQLは次のように記述されています。 パラメータの型がドメインクラスであっても、SQLファイルでの扱い方は基本型と変わりません。
select * from employee where salary > /* salary */0
Daoのメソッドは次のように呼び出します。
List<Employee> list = dao.selectBySalary(new Salary(2900));
SQLのログは次のように出力されます。
select * from employee where salary > 2900
エンティティによる検索
src/test/java/tutorial/SelectTest.javaのtestSelectByEntityメソッドを参照してください。 エンティティを条件に使用した検索です。
使用するDaoのメソッド定義は次のとおりです。
@Select List<Employee> selectByExample(Employee e);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByExample.sqlです。 SQLは次のように記述されています。 パラメータの型がエンティティクラスの場合、SQLファイルの中ではドットを使用してエンティティのプロパティにアクセスできます。
select * from employee where name = /* e.name */'aaa'
Daoのメソッドは次のように呼び出します。
Employee e = new Employee(); e.setName("SMITH"); List<Employee> list = dao.selectByExample(e);
SQLのログは次のように出力されます。
select * from employee where name = 'SMITH'
ページングを行う検索
src/test/java/tutorial/SelectTest.javaのtestOffsetLimitメソッドを参照してください。 ページングを行う検索です。
使用するDaoのメソッド定義は次のとおりです。 Daoのメソッドでは、SelectOptions型のパラメータを定義します。
@Select List<Employee> selectAll(SelectOptions options);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectAll.sqlです。 SQLは次のように記述されています。 SQLにはORDER BY句の存在が必須です。
select * from employee order by id
Daoのメソッドは次のように呼び出します。 SelectOptionsにoffsetとlimitを指定しています。
SelectOptions options = SelectOptions.get().offset(5).limit(3); List<Employee> list = dao.selectAll(options); assertEquals(3, list.size());
SQLのログは次のように出力されます。
select * from employee order by id limit 3 offset 5
ページングと集計を同時に行う検索
src/test/java/tutorial/SelectTest.javaのtestCountメソッドを参照してください。 ページングと集計(ページングしない場合の全件数の取得)を同時に行う検索です。
使用するDaoのメソッド定義は次のとおりです。 Daoのメソッドでは、SelectOptions型のパラメータを定義します。
@Select List<Employee> selectAll(SelectOptions options);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectAll.sqlです。 SQLは次のように記述されています。 SQLにはORDER BY句の存在が必須です。
select * from employee order by id
Daoのメソッドは次のように呼び出します。 SelectOptionsのcountメソッドをあらかじめ呼び出しておきます。 Daoのメソッドを呼び出した後、SelectOptionsのgetCountメソッドにより集計結果を取得できます。
SelectOptions options = SelectOptions.get().offset(5).limit(3).count(); List<Employee> list = dao.selectAll(options); assertEquals(3, list.size()); assertEquals(14, options.getCount());
SQLのログは次のように2つ出力されます。
select * from employee order by id limit 3 offset 5
select count(*) from ( select * from employee ) t_
イテレーション検索
src/test/java/tutorial/SelectTest.javaのtestIterateメソッドを参照してください。 SQLの結果セットを1件ずつエンティティなどのオブジェクトへインスタンス化する検索です。 メモリの使用量を抑えながら大量データを扱う場合に向いています。
使用するDaoのメソッド定義は次のとおりです。 @Selectのiterate要素にtrueを指定し、DaoのメソッドではIterationCallback型のパラメータを定義します。
@Select(iterate = true) <R> R selectByAge(int age, IterationCallback<R, Employee> callback);
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectByAge.sqlです。 SQLは次のように記述されています。
select * from employee where age > /* age */0 order by age
Daoのメソッドは次のように呼び出します。
Salary sum = dao.selectByAge(30, new IterationCallback<Salary, Employee>() { private Salary sum = new Salary(0); @Override public Salary iterate(Employee target, IterationContext context) { Salary salary = target.getSalary(); if (salary != null) { sum = sum.add(salary); } return sum; } });
SQLのログは次のように出力されます。
select * from employee where age > 30 order by age
結合した結果を取得する検索
src/test/java/tutorial/SelectTest.javaのtestSelectJoinedResultメソッドを参照してください。 結合した結果を取得する検索です。 結合した結果が複数テーブルのカラムを含む場合、取得対象カラムすべてに対応するプロパティをもったエンティティクラスが必要です。
使用するDaoのメソッド定義は次のとおりです。
@Select List<EmployeeDepartment> selectAllEmployeeDepartment();
戻り値のListの要素であるEmployeeDepartment
が結合した結果に対応するエンティティクラスです。
このエンティティクラスは、結合のベースとなるテーブルに対応するエンティティクラスを継承すると比較的に簡単に作成できます。
@Entity public class EmployeeDepartment extends Employee { @Column(name = "DEPARTMENT_NAME") String departmentName; ... }
Daoメソッドに対応するSQLファイルのパスはMETA-INF/tutorial/dao/EmployeeDao/selectAllEmployeeDepartment.sqlです。 SQLは次のように記述されています。
select e.*, d.name department_name from employee e left outer join department d on e.department_id = d.id order by e.id
Daoのメソッドは次のように呼び出します。
List<EmployeeDepartment> list = dao.selectAllEmployeeDepartment();
SQLのログは次のように出力されます。
select e.*, d.name department_name from employee e left outer join department d on e.department_id = d.id order by e.id