Oracleのリストの最大数を考慮したHibernate Criteria

※この投稿は、丸太式 Advent Calendar 2014の2日目です

DBMSのOracleには、IN句等に渡せるリストの最大長に制限があることは皆さんご存じだと思います。
ご存じ無い方のために例を示しますと、以下の二つのSQLでは、前者は成功し、後者は失敗します。

SELECT * FROM AnyTable WHERE AnyColumn IN (1, 2, 3, (), 1000);
SELECT * FROM AnyTable WHERE AnyColumn IN (1, 2, 3, (), 1000, 1001);
-- →ORA-01795: リストに指定できる式の最大数は1000です。

そもそもこんなSQLが発行されているのが大きな間違いですが、保守開発などどうしても大きく変更できないときは、このようにしてお茶を濁すことになります。

SELECT *FROM AnyTable WHERE AnyColumn IN (0から1000) OR AnyColumn IN (1001以降);

単純ですね。IN句を1000要素毎に分割してORでつないだだけです。

さて、ここでJavaからHibernateのCriteriaを使う場合に、Collectionの要素が1000件を超えるとやはりエラーとなってしまいます。
OracleのDialectでは対応出来ていないため、Criteriaに条件を指定する際には以下のように工夫する必要があります。

Collection<T> theCollection; // 要素数が1000を超えるかもしれないコレクション
Criteria criteria = session.createCriteria(AnyTable.class);
// criteria.add(Restrictions.in("AnyColumn", theCollection)); //←これではtheCollection.size() > 1000の場合にエラー
List<T> tmpList = new ArrayList<>(theCollection);
Junction disjunction = Restrictions.disjunction();
while (tmpList.size() > 1000) {
  List<T> subList = tmpList.subList(0, 1000);
  disjunction.add(Restrictions.in("AnyColumn", subList));
  tmpList = tmpList.subList(1000, tmpList.size());
}
if (!tmpList.isEmpty()) {
  disjunction.add(Restrictions.in("AnyColumn", tmpList));
}
criteria.add(disjunction);

面倒ですね。
コレクションを渡してdisjunctionを返すユーティリティメソッドを作っておくと多少は楽になるかもしれません。

まとめ

こんな事をするくらいなら、はじめからサブクエリを使いましょう。

今年も無謀な挑戦を始めます[Advent Calendar 2014]

※この投稿は、丸太式 Advent Calendar 2014の1日目です

無謀な挑戦=一人でAdvent Calendarを埋める

無謀ですね。
海外ではクリスマスまでのカウントダウンのために、Advent Calendarというカレンダーがあるそうです。
近年、日本のIT系技術者の間ではあるテーマに基づい12月1日から25日まで、日替わりでブログ等のエントリを書く催しがはやっています。

目的

一人でアドベントカレンダーを埋めることで、単純にアウトプットを増やすだけでなく、継続してアウトプットするきっかけにしたいと考えています。
挫折したら挫折したで、また来年がんばろうとは思いますが、去年も挫折しているので今年は毎日何かしら書きたいです。

今年のアドベントカレンダーは、「丸太式 Advent Calendar 2014」にまとめているので、気が向いたらご覧ください。

※去年の挫折した結果はこちら→http://www.adventar.org/calendars/256