概要
検索を行うには、 @Select
をDaoのメソッドに注釈します。
@Config(config = AppConfig.class) public interface EmployeeDao { @Select List<Employee> selectByDepartmentName(String departmentName); ... }
検索では、SQLファイルが必須です。 検索系のSQLを自動生成する機能はありません。
検索結果を受ける型には、基本型、ドメインクラス、エンティティクラス、java.util.Map<String, Object>
、また、それらを要素とするjava.util.List
を使用できますが、
エンティティクラスを利用する場合、エンティティクラスは検索結果に応じて作成する必要があります。
たとえば、EMPLOYEEテーブルに対応するEmployeeエンティティクラスが定義されている場合、 EMPLOYEEテーブルのカラムを含む結果セットはEmployeeエンティティクラスで受けられますが、 EMPLOYEEテーブルとDEPARTMENTテーブルを結合して得られる結果セットに対しては、Employeeエンティティクラスとは別のクラス(たとえばEmployeeDepartmentクラス)が必要です。 通常は、テーブルごとにエンティティクラスを作成し、結合して得られる結果セットに対しては、テーブルに対応するエンティティクラスを継承したクラスを作成するのがよいでしょう。 もちろん、継承せずに作成することも可能です。
問い合わせ条件
問い合わせ条件にはメソッドのパラメータを使用します。
パラメータの型には、基本型、ドメインクラス、任意の型、
および基本型やドメインクラスを要素とするjava.lang.Iterable
のサブタイプが使用できます。
パラメータの数に制限はありません。
パラメータの型が基本型もしくはドメインクラスの場合、引数をnull
にできます。
それ以外の型の場合、引数はnull
であってはいけません。
@Select List<Employee> selectByNameAndSalary(String name, BigDecimal salary);
SQLファイルではSQLコメントを使いメソッドのパラメータをSQLにマッピングさせます。 SQLコメントではメソッドのパラメータ名を参照します。
select * from employee where employee_name = /* name */'hoge' and salary > /* salary */100
メソッドのパラメータに任意の型を使用する場合は、ドット(.)でフィールドにアクセスしたりメソッドを呼び出すなどしてSQLにマッピングさせます。
@Select List<Employee> selectByExample(Employee employee);
select * from employee where employee_name = /* employee.name */'hoge' and salary > /* employee.getSalary() */100
任意の型であっても複数のパラメータを指定できます。
@Select List<Employee> selectByEmployeeAndDepartment(Employee employee, Department department);
基本型やドメインクラスを要素とするjava.lang.Iterable
のサブタイプは、
IN句を利用した検索を行う場合に使用します。java.lang.Iterable
のサブタイプは、通常java.util.List
が適切でしょう。
@Select List<Employee> selectByNames(List<String> names);
select * from employee where employee_name in /* names */('aaa','bbb','ccc')
複数件検索
複数件を検索するには、メソッドの戻り値の型をjava.util.List
にします。
List
の要素の型には、基本型、ドメインクラス、エンティティクラス、java.util.Map<String, Object>
が使用できます。
@Select List<Employee> selectByNameAndSalary(String name, BigDecimal salary);
結果が0件のときは、空のList
が返されます。null
は返されません。
ただし、@SelectのensureResult
要素にtrue
を指定した場合、
結果が0件ならばorg.seasar.doma.jdbc.NoResultException
がスローされます。
1件検索
1件を検索するには、メソッドの戻り値の型を基本型、ドメインクラス、エンティティクラス、java.util.Map<String, Object>
のいずれかにします。
@Select Employee selectByNameAndSalary(String name, BigDecimal salary);
結果が0件のときは、null
が返されます。
ただし、@SelectのensureResult
要素にtrue
を指定した場合、
結果が0件ならばorg.seasar.doma.jdbc.NoResultException
がスローされます。
結果が2件以上存在するときは、org.seasar.doma.jdbc.NonUniqueResultException
がスローされます。
イテレーションによる検索
全件を一度にList
で受け取るのではなく1件ずつ処理を行いたい場合は、
イテレーションによる検索ができます。
イテレーションによる検索を行うには、@Select
のiterate
要素をtrue
に設定し、
メソッドのパラメータにorg.seasar.doma.IterationCallback<R, T>
もしくは、
org.seasar.doma.IterationCallback<R, T>
のサブタイプを定義します。
@Select(iterate = true) Void selectByNameAndSalary(String name, BigDecimal salary, IterationCallback<Void, Employee> callback);
IterationCallback
のiterate
メソッドに検索対象がインスタンス化され1件ずつ渡されます。
EmployeeDao dao = new EmployeeDao(); dao.selectByNameAndSalary(name, salary, new IterationCallback<Void, Employee>() { @Override public Void iterate(Employee target, IterationContext context) { ... return null; } });
org.seasar.doma.IterationCallback<R, T>
の最初の型パラメータは、
Daoのメソッドの戻り値にあわせなければいけません。2番目の型パラメータは、基本型、ドメインクラス、
エンティティクラス、java.util.Map<String, Object>
のいずれかでなければいけません。
@SelectのensureResult
要素にtrue
を指定した場合、
結果が0件ならばorg.seasar.doma.jdbc.NoResultException
がスローされます。
イテレーションが完了した後に任意の処理を行うには、org.seasar.doma.IterationCallback<R, T>
の代わりに
org.seasar.doma.PostIterationCallback<R, T>
のサブタイプを定義します。
イテレーション完了後にpostIterate
が呼び出されます。
EmployeeDao dao = new EmployeeDao(); dao.selectByNameAndSalary(name, salary, new PostIterationCallback<Void, Employee>() { @Override public Void iterate(Employee target, IterationContext context) { ... return null; } @Override public Void postIterate(Void result, IterationContext context) { ... return null; } });
検索オプションを利用した検索
検索オプションのクラスSelectOptions
を使用することで、SELECT文が記述されたSQLファイルをベースにし、
ページング処理や悲観的排他制御用のSQLを自動で生成できます。
SelectOptions
は、複数件検索、
1件検索、イテレーションによる検索と組み合わせて使用します。
SelectOptions
は、Daoのメソッドのパラメータとして定義します。
@Config(config = AppConfig.class) public interface EmployeeDao { @Select List<Employee> selectByDepartmentName(String departmentName, SelectOptions options); ... }
SelectOptions
は、SelectOptions
のstaticなget
メソッドにより取得できます。
SelectOptions options = SelectOptions.get();
ページング
SelectOptions
のoffset
メソッドで開始位置、limit
メソッドで取得件数を指定し、
SelectOptions
のインスタンスをDaoのメソッドに渡します。
SelectOptions options = SelectOptions.get().offset(5).limit(10); EmployeeDao dao = new EmployeeDao(); List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options);
ページングの制約
ページングは、ファイルに記述されているオリジナルのSQLを書き換え実行することで実現されています。 オリジナルのSQLは次の条件を満たしていなければいけません。
- SELECT文である
- 最上位のレベルでUNION、EXCEPT、INTERSECT等の集合演算を行っていない(サブクエリで利用している場合は可)
- ページング処理を含んでいない
さらに、データベースの方言によっては特定の条件を満たしていなければいけません。
方言クラス | 条件 |
---|---|
org.seasar.doma.jdbc.dialect.Db2Dialect |
offsetを指定する場合、ORDER BY句を持ちORDER BY句で指定するカラムすべてをSELECT句に含んでいる。 |
org.seasar.doma.jdbc.dialect.Mssql2008Dialect |
offsetを指定する場合、ORDER BY句を持ちORDER BY句で指定するカラムすべてをSELECT句に含んでいる。 |
org.seasar.doma.jdbc.dialect.StandardDialect |
ORDER BY句を持ちORDER BY句で指定するカラムすべてをSELECT句に含んでいる。 |
悲観的排他制御
SelectOptions
のforUpdate
メソッドで悲観的排他制御を行うことを指示し、
SelectOptions
のインスタンスをDaoのメソッドに渡します。
SelectOptions options = SelectOptions.get().forUpdate(); EmployeeDao dao = new EmployeeDao(); List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options);
SelectOptions
には、ロック対象のテーブルやカラムのエイリアスを指定できるforUpdate
メソッドや、
ロックの取得を待機しないforUpdateNowait
など、名前が「forUpdate」で始まる悲観的排他制御用のメソッドが用意されています。
詳しくはJavadocコメントを参照ください。
悲観的排他制御の制約
悲観的排他制御は、ファイルに記述されているオリジナルのSQLを書き換え実行することで実現されています。 オリジナルのSQLは次の条件を満たしていなければいけません。
- SELECT文である
- 最上位のレベルでUNION、EXCEPT、INTERSECT等の集合演算を行っていない(サブクエリで利用している場合は可)
- 悲観的排他制御の処理を含んでいない
データベースの方言によっては、悲観的排他制御用のメソッドのすべてもしくは一部が使用できません。
方言クラス | 説明 |
---|---|
org.seasar.doma.jdbc.dialect.Db2Dialect |
forUpdate() を使用できる。 |
org.seasar.doma.jdbc.dialect.H2Dialect |
forUpdate() を使用できる。 |
org.seasar.doma.jdbc.dialect.HsqldbDialect |
forUpdate() を使用できる。 |
org.seasar.doma.jdbc.dialect.Mssql2008Dialect |
forUpdate() とforUpdateNoWait() を使用できる。ただし、オリジナルのSQLのFROM句は1つのテーブルだけから成らねばならない。 |
org.seasar.doma.jdbc.dialect.MysqlDialect |
forUpdate() を使用できる。 |
org.seasar.doma.jdbc.dialect.OracleDialect |
forUpdate() 、forUpdate(String... aliases) 、forUpdateNowait() 、forUpdateNowait(String... aliases) 、 forUpdateWait(int waitSeconds) 、forUpdateWait(int waitSeconds, String... aliases) を使用できる。 |
org.seasar.doma.jdbc.dialect.PostgresDialect |
forUpdate() とforUpdate(String... aliases) を使用できる。 |
org.seasar.doma.jdbc.dialect.StandardDialect |
悲観的排他制御用のメソッドすべてを使用できない。 |
集計
SelectOptions
のcount
メソッドを呼び出すことで、集計件数を取得できるようになります。
通常、ページングのオプションと組み合わせて使用し、ページングで絞り込まない場合の全件数を取得する場合に使います。
SelectOptions options = SelectOptions.get().offset(5).limit(10).count(); EmployeeDao dao = new EmployeeDao(); List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options); long count = options.getCount();
集計件数は、Daoのメソッド呼出し後に、SelectOptions
のgetCount
メソッドを使って取得します。
メソッド呼び出しの前にcount
メソッドを実行していない場合、getCount
メソッドは-1
を返します。
検索結果の保証
検索結果が1件以上存在することを保証したい場合は、@Select
のensureResult
要素にtrue
を指定します。
@Select(ensureResult = true) Employee selectById(Integer id);
検索結果が0件ならばorg.seasar.doma.jdbc.NoResultException
がスローされます。
検索結果のマッピングの保証
検索結果を受ける型がエンティティクラスやエンティティクラスを要素とするjava.util.List
の場合において、エンティティのプロパティすべてに対して漏れなく結果セットのカラムをマッピングすることを保証したい場合は、@Select
のensureResultMapping
要素にtrue
を指定します。
@Select(ensureResultMapping = true) Employee selectById(Integer id);
結果セットのカラムにマッピングされないプロパティが存在する場合org.seasar.doma.jdbc.ResultMappingException
がスローされます。
クエリタイムアウト
@Select
のqueryTimeout
要素にクエリタイムアウトの秒数を指定できます。
@Select(queryTimeout = 10) List<Employee> selectAll();
queryTimeout
要素に値を指定しない場合、
設定クラスに指定されたクエリタイムアウトが使用されます。
フェッチサイズ
@Select
のfetchSize
要素にフェッチサイズを指定できます。
@Select(fetchSize = 20) List<Employee> selectAll();
fetchSize
要素に値を指定しない場合、
設定クラスに指定されたフェッチサイズが使用されます。
最大行数
@Select
のmaxRows
要素に最大行数を指定できます。
@Select(maxRows = 100) List<Employee> selectAll();
maxRows
要素に値を指定しない場合、
設定クラスに指定された最大行数が使用されます。
マップのキーのネーミング規約
検索結果をjava.util.Map<String, Object>
にマッピングする場合、
@Select
のmapKeyNaming
要素にマップのキーのネーミング規約を指定できます。
@Select(mapKeyNaming = MapKeyNamingType.CAMEL_CASE) List<Map<String, Object>> selectAll();
MapKeyNamingType.CAMEL_CASE
は、カラム名をキャメルケースに変換することを示します。
そのほかに、カラム名を大文字や小文字に変換する規約があります。
指定しない場合、変換は行われません。