ブログトップ 記事一覧 ログイン 無料ブログ開設

水まんじゅう

 

2016-09-21

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 "になる

2016-08-16

Class#forNameはファイルシステムが大文字小文字の区別をしない場合にNoClassDefFoundErrorを投げることがある

タイトルのとおり。
通常、Class#forNameでクラスが見つからない場合はClassNotFoundExceptionが発生するが、
Windows上では以下のJUnitのテストコードのような挙動を示す。

package test;

import org.junit.Test;

public class TestMain {

  @Test
  public void test1() throws ClassNotFoundException {
    Class.forName("test.TestMain");// このクラスを呼び出す
  }

  @Test
  public void test2() throws ClassNotFoundException {
    Class.forName("java.lang.Thread");// rt.jarに入っているクラスを呼び出す
  }

  @Test(expected = ClassNotFoundException.class)
  public void test3() throws ClassNotFoundException {
    Class.forName("java.lang.thread");// rt.jarに入ってるクラスの大文字小文字の間違い
  }

  @Test(expected = NoClassDefFoundError.class)
  public void test4() throws ClassNotFoundException {
    Class.forName("test.testmain");// このクラスの大文字小文字の間違い
  }

}

まーじーかー・・・・・・
JVMバグじゃないの?

Java 1.8.0_92

2016-08-03

パーフェクトJava EEを読み始めた(パラ見した感想)

ポケモンに飽きてきたので

さて、こそこそっとパーフェクトJava EEを読み始めてみました。
とはいってもまだ1章のみ。

以前からパーフェクトJava EEが発売されるという話は聞いていて、JPAの章についてはレビューにも参加しているので、
JPAの章については安心してみてください的な感じはあります。*1

そのうち全部読むのですが、1章と全体をパラ見した感想としては、
この本のターゲットはこうですと記載されているものと、実際に読める人間には随分と隔たりがありそうだなぁという印象でした。

JSPどこいった

この本ではJSPについての説明はありません。ただ、JSPを使った説明はたくさんあります。
そのため、JSPを理解していることが前提となっており、少なくとも、これからJava EEを始めたい人が読むべき本ではないのではないかなぁと思いました。

そういえば、パーフェクトJavaのおまけにJava EEのボーナスPDFがあったなぁと思って、そちらも見直してみましたが、
そちらでもJSPの説明はなく。
http://gihyo.jp/book/2014/978-4-7741-6685-8/support

まあ、厳しいねぇ。

よくよく見てみると、第1章については上のPDFの内容を抜粋した形式となっており、さすがにちょっとさぼりすぎなのではーという印象を受けました。
一度校了をした文章を構成しなおしたからか?確認が甘かったのか、mavenダウンロードサイトの説明はあるのに、GlassFishダウンロードサイトの説明はなく、これ1冊でJava EEを始められるのか?というのは本当に疑問。
GlassFishって何よで終わる気がする。

例えば、NetBeansであればGlassFishバンドルしたインストーラーがあるので、そういったものを使用してここら辺の説明がなくても大丈夫にする方法もあるけれども、今回はそういった方法も取られておらず、asadminというのがあるよとだけ書かれており、結構きつい。

Java EE 7徹底入門ではNetBeansを使用する方法でちゃんと書かれていたので、あちらはあちらで各章の内容がちぐはぐということもあるのですが、少なくともこの本が対象としている読者層にとってはJava EE 7徹底入門のほうが良い本ではないかなぁと思いました。

しかし、巷のJava EEというか、Servlet/JSP入門書のほとんどはTomcatを前提としているので、本当はそういった本の次として置きたいのだろうけれども、どうしたもんかねぇ。

*1:さすが槙さん

2016-06-27

Java Day Kumamotoやってきた #JDK

ということで、紆余曲折ありながらも、Java Day Kumamotoという名前でカンファレンスを開催してきました。




なんと、36名登録してくれて、そのうち36名が全員来るという快挙。*1
東京じゃあ考えられない出席率。すばらしい。

大雨だし絶対参加者半分もいないんだろうなぁとか思って椅子も全員分を用意しておらず追加で用意しなければいけなかったのですが、うれしい悲鳴という感じ。

また、登壇者の方々も手弁当で来てくださってくれて本当にありがたい。

好評だったらまたやりたいですね。

あと、LTやりました。
資料は以下。

*1:キャンセルは2名ありました

2016-06-14

Java 8から増えてたExplicit receiver parameters

Java 8からこういう書き方が出来るようになっていたようです。(初めて知った)

public class Main {

    public String getString(Main this) { // thisで自分自身が引数になっている
        return this.toString();
    }
    
    public static void main(String[] args) {
        Main main = new Main();
        System.out.println(main.getString()); //呼び出すときは何も書かなくてもよい
    }
}

何がうれしいかというと、自分自身を呼び出すときにアノテーションがつけれるようになる。(そして処理が出来るようになる)
なんか、おおっ!という感じ。

呼び出すときに何も書かなくてよいというのがものすごくJavaっぽくないので驚きました。

http://blog.joda.org/2015/12/explicit-receiver-parameters.html

なお、これを使うとPMD5.4.1より前は死ぬ。
https://sourceforge.net/p/pmd/bugs/1455/


2016/06/15(追記)
コメントで何に使うのかさっぱりわからんという意見が多発していますが、それに対する回答としては



という感じで、Type Annotationを使用する場合に使用することがある。基本的には使用するケースはない。
となります。
ということで、基本的にあまりうれしくないという結論に。

便利(なのかもしれない)と思ってしまった人は勉強が足りないぞっ☆彡