OracleでIN句に1000件超のパラメータを渡す

Oracleには、リストに式を最大1000件までしか書けないという制限があるので、IN句に1001個以上のパラメータを渡すとエラーになってしまいます*1。そこで、S2JDBCで引数が1000件超の場合にIN句を分割するWhereを書いてみました。

package com.kaiseh.seasar.jdbc;

import org.seasar.extension.jdbc.Where;

/**
 * @author Kaisei Hamamoto
 */

public abstract class AbstractIn implements Where {
    private static final int MAX_EXPR_COUNT = 1000;

    private String columnName;
    private Object[] params;

    public AbstractIn(String columnName, Object... params) {
        this.columnName = columnName;
        this.params = params;
    }

    public String getCriteria() {
        StringBuilder buf = new StringBuilder();
        int clauseCount = (params.length + MAX_EXPR_COUNT - 1) / MAX_EXPR_COUNT;
        for(int i = 0; i < clauseCount; i++) {
            if(i > 0) {
                buf.append(" or ");
            }
            buf.append(columnName).append(" ").append(getConditionName()).append(" (");
            int from = MAX_EXPR_COUNT * i;
            int to = Math.min(MAX_EXPR_COUNT * (i + 1), params.length);
            for(int j = from; j < to; j++) {
                if(j > from) {
                    buf.append(", ");
                }
                buf.append("?");
            }
            buf.append(")");
        }
        return buf.toString();
    }

    public Object[] getParams() {
        return params;
    }

    protected abstract String getConditionName();
}
package com.kaiseh.seasar.jdbc;

/**
 * @author Kaisei Hamamoto
 */

public class In extends AbstractIn {
    public In(String columnName, Object... params) {
        super(columnName, params);
    }

    @Override
    protected String getConditionName() {
        return "in";
    }
}
package com.kaiseh.seasar.jdbc;

/**
 * @author Kaisei Hamamoto
 */

public class NotIn extends AbstractIn {
    public NotIn(String columnName, Object... params) {
        super(columnName, params);
    }

    @Override
    protected String getConditionName() {
        return "not in";
    }
}

*1:「そもそもIN句に1000件も渡る設計が悪い」と言われそうですが、あるエンティティのID配列を基に別テーブルを操作するケースは良くあると思います。DBFluteが1対多関連を「SQL2発で取得」しているのもこれですよね?