Select
To execute a search using the SELECT statement, annotate the DAO method with @Select
.
@Dao
public interface EmployeeDao {
@Select
List<Employee> selectByDepartmentName(String departmentName);
...
}
The @Select
annotation requires an SQL template.
Describe the SQL template in an SQL file or in the @Sql
annotation.
注釈
検索結果に応じて エンティティクラスを作成する必要があります。たとえば、EMPLOYEE テーブルに対応する Employee クラスが宣言されている場合、EMPLOYEE テーブルの列を含む結果セットは Employee クラスとして受け入れられます。ただし、EMPLOYEEテーブルとDEPARTMENTテーブルを結合して得られる結果セットには、Employeeエンティティクラスとは別のクラス (例えばEmployeeDepartmentクラス) が必要です。
検索条件
検索条件にはメソッドのパラメータを使用します。使用可能な型は以下の通りです。
任意の型
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
パラメータ数に制限はありません。パラメータの型が 基本クラス または ドメインクラス の場合、パラメータに null
を設定できます。型がそれ以外の場合、パラメータは null
であってはなりません。
基本クラスまたはドメインクラスを使用したクエリ
メソッドのパラメータとして 基本クラス または ドメインクラス を宣言します。
@Select
List<Employee> selectByNameAndSalary(String name, Salary salary);
バインド変数ディレクティブを使用してメソッドのパラメータを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);
Iterable を使用した IN 句へのマッピング
IN句へバインドするには、 java.lang.Iterable
のサブタイプを使用します。
@Select
List<Employee> selectByNames(List<String> names);
select * from employee where employee_name in /* names */('aaa','bbb','ccc')
単一レコードの検索
単一レコード検索の場合、メソッドの戻り値の型は以下のいずれかでなければいけません。
java.util.Map<String, Object>
基本クラス 、 ドメインクラス 、 エンティティクラス または java.util.Map<String, Object> のいずれかを要素とする java.util.Optional
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
@Select
Employee selectByNameAndSalary(String name, BigDecimal salary);
戻り値の型が Optional
でなく、結果が 0件 の場合は null
が返されます。 検索結果の保証 が有効になっていて結果が0件の場合は、戻り値の型に関係なく例外がスローされます。
結果が2件以上存在する場合は NonUniqueResultException
がスローされます。
複数レコードの検索
複数レコードを検索する場合はメソッドの戻り値の型に java.util.List を指定します。 List
の要素には以下の型が利用できます。
java.util.Map<String, Object>
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
@Select
List<Employee> selectByNameAndSalary(String name, Salary salary);
結果が 0件の場合、null
の代わりに空のリストが返されます。ただし、 検索結果の保証 が有効であるならば 0件の場合は例外がスローされます。
ストリーム検索
すべてのレコードを少しづつ処理する場合は java.util.stream.Stream
を使ったストリーム検索を使用できます。
ストリーム検索には戻り値を返す方法と java.util.Function
に Stream
を渡す方法の 2 種類があります。
FunctionにStreamを渡す方法
@Select
アノテーション内の strategy
プロパティに SelectType.STREAM
を設定し、 java.util.Function<Stream<TARGET, RESULT>
のサブタイプをメソッドのパラメータに追加します。
@Select(strategy = SelectType.STREAM)
BigDecimal selectByNameAndSalary(String name, BigDecimal salary, Function<Stream<Employee>, BigDecimal> mapper);
呼び出し元はストリームを受け取り、結果を返すラムダ式を渡します。
EmployeeDao dao = new EmployeeDaoImpl();
BigDecimal result = dao.selectByNameAndSalary(name, salary, stream -> {
return ...;
});
Function<Stream<TARGET>, RESULT>
に対応する型パラメータ TARGET
は以下のいずれかである必要があります。
java.util.Map<String, Object>
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
型パラメータ RESULT
は DAO メソッドの戻り値と一致する必要があります。
検索結果の保証 が有効な場合、検索結果が 0件の場合に例外がスローされます。
戻り値を返す
メソッドの戻り値に java.util.stream.Stream
を定義します。 Stream
ので型パラメータには以下のものを指定できます。
java.util.Map<String, Object>
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
@Select
Stream<Employee> selectByNameAndSalary(String name, BigDecimal salary);
呼び出し元のコードは以下のようになります。
EmployeeDao dao = new EmployeeDaoImpl();
try (Stream<Employee> stream = dao.selectByNameAndSalary(name, salary)) {
...
}
検索結果の保証 が有効な場合、検索結果が 0件の場合に例外がスローされます。
警告
リソースの解放忘れを防ぐため、必ずストリームをクローズしてください。ストリームを閉じないと、 java.sql.ResultSet
、 java.sql.PreparedStatement
、 java.sql.Connection
などが閉じられません。
注釈
戻り値を返すのはリソースの解放忘れの危険があるため、特別な理由がない限りFunctionへStreamを渡す方法を検討してください。 Domaは、DAO メソッドに警告メッセージを表示して注意を促します。警告を抑制するには以下のように @Suppress
を指定します。
@Select
@Suppress(messages = { Message.DOMA4274 })
Stream<Employee> selectByNameAndSalary(String name, BigDecimal salary);
コレクト検索
結果を java.util.Collector
として扱う場合は、コレクト検索を使用できます。
コレクト検索を実施するには、 @Select
の strategy
要素に SelectType.COLLECT
を設定し、 メソッドのパラメータに java.stream.Collector<TARGET, ACCUMULATION, RESULT>
もしくは java.stream.Collector<TARGET, ?, RESULT>
のサブタイプを定義します。
@Select(strategy = SelectType.COLLECT)
<RESULT> RESULT selectBySalary(BigDecimal salary, Collector<Employee, ?, RESULT> collector);
呼び出し元は Collector
のインスタンスを渡します。
EmployeeDao dao = new EmployeeDaoImpl();
Map<Integer, List<Employee>> result =
dao.selectBySalary(salary, Collectors.groupingBy(Employee::getDepartmentId));
Collector<TARGET, ACCUMULATION, RESULT>
の型パラメータ TARGET
は次のいずれかでなければいけません。
java.util.Map<String, Object>
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
型パラメータ RESULT
はDAOのメソッドの戻り値に合わせなければいけません。
検索結果の保証 が有効な場合、検索結果が 0件の場合に例外がスローされます。
注釈
コレクト検索はストリーム検索のFunctionに渡す方法のショートカットです。ストリーム検索で得られる Stream
オブジェクトの collect
メソッドを使って同等のことができます。
検索オプションの使用
検索オプションを表す SelectOptions
を使用すると、SELECT ステートメントからページングや悲観的排他制御用の SQL を自動生成できます。
SelectOptions
は、 単一レコード検索 、 複数レコード検索 、 ストリーム検索 と組み合わせて使用します。
SelectOptions
は、DAOのメソッドのパラメータとして定義します。
@Dao
public interface EmployeeDao {
@Select
List<Employee> selectByDepartmentName(String departmentName, SelectOptions options);
...
}
SelectOptions
のインスタンスは、staticな get
メソッドにより取得できます。
SelectOptions options = SelectOptions.get();
ページング
ページングを実行するには、 SelectOptions
の offset
メソッドで開始位置、 limit
メソッドで取得件数を指定し、 SelectOptions
のインスタンスを DAO メソッドに渡します。
SelectOptions options = SelectOptions.get().offset(5).limit(10);
EmployeeDao dao = new EmployeeDaoImpl();
List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options);
ページングは、ファイルに記述されているオリジナルのSQLを書き換え実行することで実現されています。 オリジナルのSQLは次の条件を満たしていなければいけません。
SELECT ステートメントである
最上位のレベルでUNION、EXCEPT、INTERSECT等の集合演算を行っていない (サブクエリで利用している場合は可)
ページング処理を含んでいない
さらに、ダイアレクト に従って特定の条件を満たす必要があります。
ダイアレクト |
条件 |
---|---|
Db2Dialect |
offset を指定する場合は、ORDER BY 句で指定されたすべての列が SELECT 句に含まれる |
Mssql2008Dialect |
offset を指定する場合は、ORDER BY 句で指定されたすべての列が SELECT 句に含まれる |
MssqlDialect |
offset を指定する場合は、ORDER BY 句が存在する |
StandardDialect |
ORDER BY 句があり、ORDER BY 句で指定されたすべての列が SELECT 句に含まれる |
悲観的排他制御
SelectOptions
の forUpdate
メソッドで悲観的排他制御を行うことを示し、SelectOptionsのインスタンスをDAOのメソッドに渡します。
SelectOptions options = SelectOptions.get().forUpdate();
EmployeeDao dao = new EmployeeDaoImpl();
List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options);
SelectOptions
には、ロック対象のテーブルやカラムのエイリアスを指定できる forUpdate
メソッドや、
ロックの取得を待機しない forUpdateNowait
など、名前が forUpdate
で始まる悲観的排他制御用のメソッドが用意されています。
悲観的排他制御は、オリジナルのSQLを書き換えて実行しています。 オリジナルのSQLは次の条件を満たしていなければいけません。
SELECT ステートメントである
最上位のレベルでUNION、EXCEPT、INTERSECT等の集合演算を行っていない(サブクエリで利用している場合は可)
悲観的排他制御の処理を含んでいない
ダイアレクトによっては、悲観的排他制御用のメソッドのすべてもしくは一部が使用できません。
ダイアレクト |
説明 |
---|---|
Db2Dialect |
forUpdate()を使用できる |
H2Dialect |
forUpdate()を使用できる |
HsqldbDialect |
forUpdate()を使用できる |
Mssql2008Dialect |
forUpdate()とforUpdateNoWait()を使用できる。 ただし、オリジナルのSQLのFROM句は1つのテーブルだけから成らねばならない。 |
MysqlDialect |
forUpdate() を使用できる |
OracleDialect |
forUpdate()、forUpdate(String... aliases)、forUpdateNowait()、forUpdateNowait(String... aliases)、forUpdateWait(int waitSeconds)、forUpdateWait(int waitSeconds, String... aliases) を使用できる |
PostgresDialect |
forUpdate() および forUpdate(String... エイリアス) を使用できる |
StandardDialect |
悲観的排他制御用のメソッドすべてを使用できない |
集計
SelectOptions
の count
メソッドを呼び出すことで集計件数を取得できるようになります。
通常、ページングのオプションと組み合わせて使用し、ページングで絞り込まない場合の全件数を取得する場合に使います。
SelectOptions options = SelectOptions.get().offset(5).limit(10).count();
EmployeeDao dao = new EmployeeDaoImpl();
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件ならば NoResultException
がスローされます。
検索結果のマッピングの保証
エンティティのプロパティすべてに対して漏れなく結果セットのカラムをマッピングすることを保証したい場合は、 @Select
の ensureResultMapping
要素に true
を指定します。
@Select(ensureResultMapping = true)
Employee selectById(Integer id);
結果セットのカラムにマッピングされていないプロパティがある場合、 ResultMappingException
がスローされます。
クエリタイムアウト
@Select
アノテーション内の queryTimeout プロパティにクエリタイムアウトの秒数を指定できます。
@Select(queryTimeout = 10)
List<Employee> selectAll();
queryTimeout
プロパティの値が設定されていない場合は、 設定 で指定されたクエリタイムアウトが使用されます。
フェッチサイズ
フェッチサイズは @Select
アノテーション内の fetchSize
プロパティに指定できます。
@Select(fetchSize = 20)
List<Employee> selectAll();
値が設定されていない場合は、 設定 で指定されたフェッチ サイズが使用されます。
最大行数
最大行数は @Select
アノテーション内の maxRows
プロパティに指定できます。
@Select(maxRows = 100)
List<Employee> selectAll();
値が設定されていない場合は、設定 で指定された最大行数が使用されます。
マップのキーの命名規則
検索結果を java.util.Map<String, Object>
にマッピングする場合、
@Select
の mapKeyNaming
要素にマップのキーの命名規約を指定できます。
@Select(mapKeyNaming = MapKeyNamingType.CAMEL_CASE)
List<Map<String, Object>> selectAll();
MapKeyNamingType.CAMEL_CASE
は、カラム名をキャメルケースに変換することを示します。
そのほかにカラム名を大文字や小文字に変換する規約があります。
最終的な変換結果は、ここに指定した値と 設定 に指定された
MapKeyNaming
の実装により決まります。
SQLログの出力形式
@Select
の sqlLog
要素に SQL のログ出力形式を指定できます。
@Select(sqlLog = SqlLogType.RAW)
List<Employee> selectById(Integer id);
SqlLogType.RAW
はバインドパラメータ付きの SQL をログ出力することを表します。