ASPの機能拡張をしていたところ、HibernateではSQL文にビット演算子が使えないことが判明。
色々と調べてみたが、日本語での解説がなかったので、参考までに記録しておく。
まず、Hibernateの2.xではビット演算子がサポートされていた(ようだ)が、3.xからサポートされないとのこと。
そこで、ビット演算をユーザ関数として定義し、データベースへの接続を行うDialectをオーバーラップして、直接SQL文を吐き出すようにするとよいらしい。
と、ここまでの内容でなるほど!と思った方はすでにご自分で解決できるだろうし、そもそも書いている僕自身がこれじゃさっぱりわからないw。
では具体的な対策を説明していきます。
調べる内容は概ね次の通り。
- Hibernateで利用しているデータベースがビット演算子をサポートしていること。
- サポートしているビット演算子(通常は'&'とか)
- Hibernate内における利用しているデータベースのDialectのクラス名
1.と2.は大丈夫ですよね。ちなみに僕の環境はSQLServer2005ですが、ビット演算子(AND)は「&」です。
3.については、Hibernateのコンフィグレーションファイル(「hibernate.cfg.xml」など、データベースの接続情報を記載するxmlファイルがあるよね、あれです)に書かれているdialectプロパティを見ると分かります。ちなみにSQLServerは、「org.hibernate.dialect.SQLServer」でした。
で、実際には2つクラスファイルを作成する必要があります。以下、SQLServerを例に説明するので、他のデータベースを使っている人は適宜読み替えましょう。
まずは1つ目のクラス「SQLServerDialect.java」。
クラス名は、先程3.で調べたものと併せておきます。
packageパッケージ名;
import org.hibernate.Hibernate;
public class SQLServerDialect extends org.hibernate.dialect.SQLServerDialect {
public SQLServerDialect() {
super();
registerFunction("bitwise_and", new BitwiseAndFunction("bitwise_and", Hibernate.INTEGER));
}
}
ポイントは、先程3.で調べたDialectを派生(extends)させること。
「bitwise_and」というユーザ関数を追加しているわけです。
2つ目のクラスは、「BitwiseAndFunction.java」。
package パッケージ名;
import java.util.List;
import org.hibernate.QueryException;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.type.Type;
public class BitwiseAndFunction
extends StandardSQLFunction implements SQLFunction {
public BitwiseAndFunction(String name) {
super(name);
}
public BitwiseAndFunction(String name, Type type) {
super(name, type);
}
public String render(List args, SessionFactoryImplementor factory)
throws QueryException {
if (args.size() != 2) {
throw new IllegalArgumentException("the function must be passed 2 arguments");
}
StringBuffer buffer = new StringBuffer(args.get(0).toString());
buffer.append(" & ").append(args.get(1)); // ←ここにビット演算子を指定
return buffer.toString();
}
}
このクラスは、ユーザ関数「bitwise_and」の中身を受け持ちます。
ポイントは、returnする直前に指定しているビット演算子をデータベースに合わせること。今回は” & ”としてあります。
最後に、先程3.で調べるときに参照したHibernateコンフィグレーションファイルの、Dialectプロパティを、今作成したDialectクラスに書き換えればOKです。
実際の使い方ですが、例えばHQL上で
where bitwise_and( X, 1 ) = 1
のように書いてしまえばOKで、これで実際のSQL上は、
where X & 1 = 1
と送られることになります。
とりあえず僕はこれでうまくいきました。
参考にしたのは、以下のサイトです。
http://javabeans.asia/2008/05/06/bitwise_operation_in_hibernate_3.html