JDBCでOracleのchar型のカラムを検索する。

char型は桁数に足りない場合、スペースで勝手に埋められます。*1

検索する場合は、空白スペースを埋めた状態で検索してあげる必要があります。

ここで、OracleJDBCドライバに入っているPreparedStatementの実装、OraclePreparedStatementのメソッドsetFixedCharを使用することで、空白で埋めるのを適切に行ってくれます。

Oracleの公式ドキュメントを見るとPreparedStatementをOraclePreparedStatementにキャストして使用してくださいと記載してあります。


データベース内のCHARデータは、列幅まで埋め込まれます。このため、SELECT文のWHERE句に文字データをバインドするためのsetCHARメソッドの使用に関して、制限が生じます。WHERE句の文字データも、SELECT文で合致させるために、列幅まで埋め込む必要があります。これは特に列幅がわからない場合に問題になります。

これを修正するために、OracleはOraclePreparedStatementクラスにsetFixedCHARメソッドを追加しました。このメソッドは埋込みなしの比較を実行します。

注意:
setFixedCHARメソッドを使用するには、必ずプリコンパイルされたSQL文オブジェクトをOraclePreparedStatementにキャストしてください。
INSERT文で、setFixedCHARを使用する必要はありません。データベースは挿入時に、常にそのデータを列幅まで自動的に埋め込みます。

https://docs.oracle.com/cd/E16338_01/java.112/b56281/datacc.htm#BABCHGCH

これには問題があります。
それはPreparedStatementが何かの理由でProxyされている場合にOraclePreparedStatementにキャストが出来ないことがあることです。
アプリケーションサーバー組込のDataSource実装を使用しているとよくあります。

そこで次のようなコードを考えます。

OraclePreparedStatement orapstmt = (OraclePreparedStatement)pstmt.unwrap(PreparedStatement.class);

unwrapはJava 6から増えたメソッドでJavaDocには次のような記載があります。

標準以外のメソッド、またはプロキシによって公開されない標準メソッドにアクセスできるようにするために、指定されたインタフェースを実装しているオブジェクトを返します。レシーバがこのインタフェースを実装している場合、結果はレシーバ、またはレシーバのプロキシになります。レシーバがラッパーであり、ラップされたオブジェクトがインタフェースを実装している場合、結果はラップされたオブジェクト、またはそのプロキシです。それ以外の場合は、ラップされたオブジェクト、またはその結果のプロキシに対してunwrapを再帰的に呼び出した結果が返されます。レシーバがインタフェースを実装しておらず、ラッパーでもない場合は、SQLExceptionがスローされます。

https://docs.oracle.com/javase/jp/8/docs/api/java/sql/Wrapper.html


これでOKかと思ったら、別の問題がありました。
それは、Wrapper自体がJava 6から実装されたもので、古くからあるDataSourceでは正しく実装されてない場合があるということです。(例えばうぇぶなんとかというアプリケーションサーバーのDataSource実装とか)
そもそも、何らかの理由でラップされているものを直接使ってしまってよいのかという問題もあります。

さて、どうするか

PreparedStatementには値として直接文字列をバインドするsetStringメソッド以外にsetObjectメソッドがあります。
そのsetObjectメソッドは第三引数にjava.sql.Typesで定義された値を渡すことで、型が何かを指定することができます。
例えば、setStringと同じ挙動をさせるためには、java.sql.Typesで定義されたVARCHARもしくはNVARCHARあたりを渡してあげればよいです。

これでsetFixedCharの挙動をさせたい場合は、999を渡してあげます。

pstmt.setObject(index, "string", 999);

しかしながら、この999自体はjava.sql.Typesには定義されていません。

定数フィールド値一覧
https://docs.oracle.com/javase/jp/8/docs/api/constant-values.html#java.sql.Types.ARRAY


どこで定義されていると、Oracleのドライバ内のoracle.jdbc.OracleTypesで定義されています。
https://docs.oracle.com/cd/E16338_01/appdev.112/e13995/oracle/jdbc/OracleTypes.html#FIXED_CHAR

Use this type when binding to a CHAR column in the where clause of a Select statement. A non padded comparision will be done unlike in CHAR and VARCHAR case. Not particularly needed for an insert as the database will pad it. This type is used for bind only. It cannot be used for define and registerOutParameter.

ということで、これを使ってあげるのが一番確実のようです。
もちろんOracle以外では使えませんのでご注意を。

*1:例えば、char(8)で定義したカラムに"abc"を入れると"abc     "になる