検索
SELECTステートメントを使用して検索を実行するには、DAOメソッドに @Select
アノテーションを付けます。
@Dao
public interface EmployeeDao {
@Select
List<Employee> selectByDepartmentName(String departmentName);
...
}
@Select
アノテーションには SQLテンプレート が必要です。SQLファイルまたは @Sql
アノテーションでSQLテンプレートを記述してください。
注釈
検索結果に応じて エンティティクラスを作成する必要があります。たとえば、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);
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
が返されます。
検索結果が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);
検索結果がない場合、空のリストが返されます。
ストリーム検索
大量のレコードをインクリメンタルに処理するために、java.util.stream.Stream
を使ったストリーム検索を利用できます。
ストリーム検索には戻り値を返す方法と java.util.Function
に Stream
を渡す方法の 2 種類があります。
関数にストリームを渡す
@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);
DAOメソッドの呼び出し元は、ストリームを受け取って結果を返すラムダ式を渡します。
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 メソッドの戻り値と一致する必要があります。
ストリームを返す
メソッドの戻り値に 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);
DAOメソッドの呼び出し元は以下のとおりです。
EmployeeDao dao = new EmployeeDaoImpl();
try (Stream<Employee> stream = dao.selectByNameAndSalary(name, salary)) {
...
}
警告
java.sql.ResultSet
、 java.sql.PreparedStatement
、 java.sql.Connection
などのリソースを適切に閉じるために、必ず Stream
を閉じてください。
注釈
値を返すときにリソースを解放することを忘れるリスクがあるため、Domaは警告メッセージを表示します。 警告メッセージを抑制するには、@Supress
を以下のように指定してください。
@Select
@Suppress(messages = { Message.DOMA4274 })
Stream<Employee> selectByNameAndSalary(String name, BigDecimal salary);
コレクター検索
検索結果は java.util.Collector
を使用して処理できます。
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);
DAOメソッドの呼び出し元は Collector
のインスタンスを渡します。
EmployeeDao dao = new EmployeeDaoImpl();
Map<Integer, List<Employee>> result =
dao.selectBySalary(salary, Collectors.groupingBy(Employee::getDepartmentId));
Function<TARGET, ACCUMULATION, RESULT>, RESULT>
の型パラメータ TARGET
は以下のいずれかである必要があります。
java.util.Map<String, Object>
java.util.OptionalInt
java.util.OptionalLong
java.util.OptionalDouble
Collector<TARGET, ACCUMULATION, RESULT>
の型パラメータの RESULT
は、DAOメソッドの戻り値の型と一致する必要があります。
注釈
コレクト検索はストリーム検索のFunctionに渡す方法のショートカットです。ストリーム検索で得られる Stream
オブジェクトの collect
メソッドを使って同等のことができます。
検索オプション
SelectOptions
を使用することで、SELECTステートメントをページングや悲観的排他制御のためのSQLへ変換することが可能です。
SelectOptions
はDAOメソッドのパラメータとして定義されます。
@Dao
public interface EmployeeDao {
@Select
List<Employee> selectByDepartmentName(String departmentName, SelectOptions options);
...
}
静的な get
メソッドを使用して SelectOptions
のインスタンスを取得できます。
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 options = SelectOptions.get().forUpdate();
EmployeeDao dao = new EmployeeDaoImpl();
List<Employee> list = dao.selectByDepartmentName("ACCOUNT", options);
SelectOptions
は、 forUpdate で始まる名前を持つ悲観的排他制御のためのメソッドを提供します。 ロックされるテーブルや列のエイリアスを指定する forUpdate
や ロックの取得を待機しない forUpdateNowait
など。
Pessimistic concurrency control は、元の SQL を書き換えることによって達成されます。これは、以下の条件を満たす必要があります。
SELECT ステートメントである
最上位のレベルでUNION、EXCEPT、INTERSECT等の集合演算を行っていない (サブクエリで利用している場合は可)
楽観的排他制御の処理を含んでいない
ダイアレクトによっては、悲観的排他制御の一部またはすべての方法が使用できない場合があります。
ダイアレクト |
説明 |
---|---|
Db2Dialect |
forUpdate()を使用できる |
H2Dialect |
forUpdate()を使用できる |
HsqldbDialect |
forUpdate()を使用できる |
Mssql2008Dialect |
forUpdate()とforUpdateNoWait()を使用できる。 ただし、オリジナルのSQLのFROM句は1つのテーブルだけから成らねばならない。 |
MysqlDialect |
forUpdate() を使用できる |
MysqlDialect (V8) |
forUpdate(), forUpdate(String... aliases), forUpdateNowait(), forUpdateNowait(String... aliases) を使用できる |
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
メソッドがDAO メソッドの呼び出しより前に実行されていない場合、getCount
メソッドは -1 を返します。
検索結果の保証
検索結果が少なくとも 1 つあることを保証したい場合は、@Select
の ensureResult
要素に true
を指定します。
@Select(ensureResult = true)
Employee selectById(Integer id);
検索結果がない場合は、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();
fetchSize
プロパティの値が設定されていない場合、 設定 で指定されたフェッチサイズが使用されます。
最大行数
@Select
アノテーションの maxRwos
プロパティで、最大行数を指定できます。
@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
は、カラム名がキャメルケースに変換されることを示します。カラム名を大文字または小文字に変換する規約もあります。
最終的な変換結果は、ここで指定された値と 設定 で指定された MapKeyNaming
の実装によって決定されます。
SQLログの出力形式
@Select
アノテーションの sqlLog
要素でSQLログの出力形式を指定できます。
@Select(sqlLog = SqlLogType.RAW)
List<Employee> selectById(Integer id);
SqlLogType.RAW
は、バインドパラメータでSQLをログに記録することを示します。