Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

OracleでBLOB型の項目にデータを登録したい。

javaを1.4から6にバージョンアップしたところ、
うまくBLOB型の登録ができなくなってしまった
のでお力をお貸し下さい。

【バージョン】
oracle9
java6


【ソース】
-----------------------------------------------------
sqlBuffer = new StringBuffer();
    
sqlBuffer.append("insert into NSAM values(");
sqlBuffer.append("         " + patientNo);
sqlBuffer.append("        ," + sumNo);
sqlBuffer.append("        ," + amNo);
sqlBuffer.append("        , empty_blob()");
sqlBuffer.append(") ");


stmt.execute(sqlBuffer.toString());
                

sqlBuffer = new StringBuffer();

sqlBuffer.append("select");
sqlBuffer.append("     PATIENT_NO");
sqlBuffer.append("    ,SUM_NO");
sqlBuffer.append("    ,AM_NO");
sqlBuffer.append("    ,AM");
sqlBuffer.append(" from");
sqlBuffer.append("    NSAM");
sqlBuffer.append(" where");
sqlBuffer.append("        PATIENT_NO = " + patientNo);
sqlBuffer.append("    and SUM_NO = " + sumNo);
sqlBuffer.append("    and AM_NO = " + amNo);
sqlBuffer.append(" for update");

resultSet = stmt.executeQuery(sqlBuffer.toString());
resultSet.next();

Blob blobData = resultSet.getBlob("AM");

OutputStream out = blobData.setBinaryStream(1);

byte[] buffer = new byte[4096];
int length = -1;                
while((length = fileStream.read(buffer)) != -1){
    out.write(buffer, 0, length);
}
                
out.close();
------------------------------------------------------


ログをいれて試してみたところ、
Blob blobData = resultSet.getBlob("AM");            
の部分の取得がうまくいっていないようです。
java1.4の時は、
BLOB blobData = ((OracleResultSet)((DelegatingResultSet)resultSet).getDelegate()).getBlob("AM");
で取得できていたのですが…

oracle.sql.BLOBって、java6からはエラーになるのですが
使用できないのでしょうか?
今は、java.sql.Blobを使用しています。

よろしくお願いします。

34

回答

104497

閲覧

34件の回答

評価

0

> OutputStream out = blobData.setBinaryStream(1);
ここ、やりたい事が逆じゃない?

InputStream is = blobData.getBinaryStream();
して、isから読み込むんだと思うけど。

評価

0

InputStream is = blobData.getBinaryStream();

上のソースを書いてみたところ、コンパイルが通りません!
setBinaryStreamには必ず引数が必要で、戻り値の型が
OutputStreamになります。

評価

0

setじゃなくてgetなんだけど

http://docs.oracle.com/javase/jp/6/api/java/sql/Blob.html

setBinaryStreamは、Blobに書き込むためのもの

評価

0

すみません。getでした。

がしかし、今やりたい事はBlobに書き込みが
したいのですが…。
説明不足ですみません。

readされているfileStreamがInputStream型になります。
こちらは、引数で渡しています。

もう少し説明すると、画面でファイルを設定しそれを
読み込んでInputStreamで渡し、それをOracleデータベースの
BLOB型に登録したいという流れです。

評価

0

JDBC4.0では、oracle.sql.BLOBを使わなくとも、
java.sql.Blobで対応できるっぽいので

PreparedStatement stmt = conn.prepareStatement(
  "update NSAM "
  + " set AM = ? "
  + "where 
  + "  PATIENT_NO = ? "
  + "  and SUM_NO = ? "
  + "  and AM_NO = ? ");
stmt.setBinaryStream(1, fileStream);  
stmt.setString(2, patientNo);  
stmt.setString(3, sumNo);  
stmt.setString(4, amNo);  
stmt.executeUpdate();  
stmt.close();

とかできない?

評価

0

//サマリ添付資料に空データの登録
sqlBuffer = new StringBuffer();

sqlBuffer.append("insert into SUMAM values(");
sqlBuffer.append("         " + patientNo);
sqlBuffer.append("        ," + sumNo);
sqlBuffer.append("        ," + amNo);
sqlBuffer.append("        , empty_blob()");
sqlBuffer.append(") ");

stmt.execute(sqlBuffer.toString());
    

//サマリ添付資料のセット
PreparedStatement stmt2 = conn.prepareStatement(
          "update SUMAM "
          + " set AM = ? "
          + "where "
          + "  PATIENT_NO = ? "
          + "  and SUM_NO = ? "
          + "  and AM_NO = ? ");


stmt2.setBinaryStream(1, fileStream, (int)amFileName.length());  

stmt2.setString(2, patientNo);  
stmt2.setString(3, sumNo);  
stmt2.setString(4, amNo); 
stmt2.executeUpdate();  

stmt2.close();            

conn.commit();


これでとりあえず動きました!!
有難うございます。

でも、今度はちゃんとDBに格納されているか
テストの為DBからselectでBLOB型データを取得
して表示している場所があるんですが、
そこの処理でエラー発生…。

なかなかスムーズにいかないですねぇ。


StringBuffer sqlBuffer = new StringBuffer();

sqlBuffer.append("select");
sqlBuffer.append("     PATIENT_NO");
sqlBuffer.append("    ,SUM_NO");
sqlBuffer.append("    ,AM_NO");
sqlBuffer.append("    ,AM");
sqlBuffer.append(" from");
sqlBuffer.append("    SUMAM");
sqlBuffer.append(" where");
sqlBuffer.append("        PATIENT_NO = " + patientNo);
sqlBuffer.append("    and SUM_NO = " + sumNo);
sqlBuffer.append("    and AM_NO = " + amNo);

stmt = conn.createStatement();
resultSet = stmt.executeQuery(sqlBuffer.toString());

InputStream inputStream = null;

if(resultSet.next()){

    ContentStateBean stateBean = new ContentStateBean();
    
    stateBean.setPatientNo(resultSet.getString("PATIENT_NO"));
    stateBean.setSumNo(resultSet.getString("SUM_NO"));
    stateBean.setAmNo(resultSet.getString("AM_NO"));            
    stateBean.setAm((Blob)(resultSet.getBlob("AM")));
}


stateBean.setAm((Blob)(resultSet.getBlob("AM")));の部分で、

java.lang.AbstractMethodError: oracle.jdbc.driver.OracleResultSetImpl.getBlob(Ljava/lang/String;)Ljava/sql/Blob;

のエラーが出てしまいます。

評価

0

AbstractMethodErrorって事は、メソッドの実装が無いんだね。

ここの最後の方
http://otndnld.oracle.co.jp/document/products/oracle11g/111/doc_dvd/java.111/E05720-02/jdbcvers.htm
「LOBの作成」のあたりを見ると、
> これらのオブジェクトからデータを取り出すには、ResultSetおよびCallableStatementインタフェースで定義されている
getBlob、getClobおよびgetNClobメソッドをコールします。
とあるんだけど、なんでgetBlobが実装されていないんだろう?

使用しているドライバーが古かったりする?
> 注意:
> JDBC 4.0を標準サポートするには、classpath環境変数でojdbc6*.jarを指定しておく必要があります。

評価

0

まさしくドライバ古いです!

oracleも未だ9iを使っていて、
ドライバはojdbc14.jarです。

JDBCドライバとoracleのバージョンって
互換性はないのでしょうかねぇ?

もしないんだとしたら、ドライバの
バージョン上げてしまおうかな…。

最新のドライバにしてしまって
大丈夫でしょうか?

評価

0

いい忘れました!

先日javaのバージョンを1.4から6に
上げたので、java6に対応するドライバを
入れなきゃいけないという事ですね。

試してみます。
本当にありがとうございます。

試してみたら、また結果書き込みますね!

評価

0

oracleのドライバjdbc6に入れ替えてみましたが…
ダメでした。

んー…。

でも、現在eclipseで開発中なんですが、
resultSet.と打つとgetBlobコマンドが選択肢に
表示されるので、これはjdbcドライバは対応
されているという事になるのでしょうか?

これは単にjava6で動かしているからですかねぇ。

評価

0

大抵の場合は抽象クラスもしくはインターフェースで扱うので、個々の実装クラス
の事情は考慮されない。
選択肢に出るのは単にそのメソッドが存在するだけで、それが実装クラスにおいて
期待どおり実装されているかは保証しない。

例外なしに、単に登録されない?
元になるInputStreamからちゃんと取れることは確認済み?
BLOB以外は同ロジックで問題なく登録される?

評価

0

>選択肢に出るのは単にそのメソッドが存在するだけで、それが実装クラスにおいて
>期待どおり実装されているかは保証しない。
そうなんですねぇ。
ありがとうございます。


>例外なしに、単に登録されない?
すみません。これはどういう意味でしょうか?

>元になるInputStreamからちゃんと取れることは確認済み?
これは間違いないと思います。
今回の処理は元々はjava1.4できちんと動いていた処理で
それをjava6にあげたところBlobの部分だけがエラーになったので。

>BLOB以外は同ロジックで問題なく登録される?
はい。データベースの中身をobjectbrowserで確認したので
きちんと入ってました。

BLOB以外は同ロジックで問題なく登録される?

評価

0

getBlobでjava.sql.Blobを取り出さずに、
getBinaryStreamでInputStreamを取り出したら読み込める気がする。

元々BLOBの操作については、DB毎にまちまちで、統一されていなかった経緯があるので、
oracle.sql.BLOBにキャストして〜なんて操作が必要だったんだけど、
JDBC4.0的には、java.sql.Blobを経由して操作するはずなんだよね。

Blob blob = conn.createBlob();
OutputStream os = 〜
blob.setBynaryStream(os);
os.write等して
stmt.setBlob(blob);
みたいな感じで

読み込みも、
Blob blob = resultSet.getBlob();
InputStream is = blob.getBinaryStream();

ちょっと、検証してる時間が取れないので、嘘言ってるかもしれないです。

評価

0

書き込みの部分から修正した方が良いという
事でしょうか?

読み込みの部分はもう既にInputStreamを
しようしたロジックになっていますが、
処理がそこまでたどりついていない状態です。

【ソース】
strutesを使用しています。

beanでstatebeanに読み込みファイルをセット
stateBean.setAm((Blob)(resultSet.getBlob("AM")));


次に、セットされたファイル情報を元にservletで
画像を取得

//statebeanからファイルデータの取得
Blob fileStream    = ((ContentStateBean)sumAmBean.get(sumAmBean.getSomePrimaryKeys().get(0))).getAm();
        
InputStream byte_stream    = fileStream.getBinaryStream();
byte[] byte_array        = new byte[fileSize];
byte_stream.read(byte_array);
        
String defaultFileName    = "";

//拡張子の有無判定
if(extension == null || extension.trim().length() == 0){
 defaultFileName    = amNo;
}else{
 defaultFileName    = amNo + "." + extension;
}

//ファイルのダウンロード
aResponse.setContentType(contType + "; charset=Shift_JIS");
aResponse.setHeader("Content-Disposition", "inline;filename=" + defaultFileName);

try{
 aResponse.getOutputStream().write(byte_array);
}catch(Exception e){
 log.error(aRequest.getRemoteAddr() + " - " + "コンテンツ標準表示エラー:(" + e.toString() + ")");
}


このソースのbeanでstatebeanに読み込みファイルをセット
している部分で既にエラーとなってしまいます。

評価

0

いえ、getBlobで取得できないのですから、
なんかしら他の要因で、setBlobもできなくなってると思います。
ですから、setBinaryStreamを使った方式で良いと思いますよ。

それに、変更にかかる費用や信頼性の低下を補って
余りある利点が無ければ、
既に動いてる物は無理に直さないのが慣例ですし。

評価

0

setBlobというものを使用している部分が
ソースにはないのですが…。

すみません。
あまり理解できてなくて…。

どこを直せばよいのでしょうか?

評価

0

getBlobしてるサーブレットと、画像をDLさせるサーブレットでリクエストが別だから、
このロジックだと問題になりそう。

getBlobした段階で、自動でBLOBの中身をDBから吸い出して、
どこかに退避してくれている訳ではありません。

元のサーブレットではBlobを抽出せずに、
画像をDLさせるサーブレットの方で、改めて抽出条件を指定して、
ResultSetからgetBinaryStreamして返した方が良いと思います。
その方がテストも別々にできるしね。

それと、画像をDLさせるのに、Content-Typeにcharset=Shift_JISつける必要はないよ。

評価

0

>getBlobした段階で、自動でBLOBの中身をDBから吸い出して、
>どこかに退避してくれている訳ではありません。
statebeanにセットしてstatebeanから取得してもダメですか?

元のサーブレットはbean側の事でしょうか?

bean側ではresultSetをservletに渡す?
そして、servlet側でresultSet.getBinaryStreamで画像データを
取得するという事でしょうか?

解釈合ってますか?
間違っていたらすみません。

>それと、画像をDLさせるのに、Content-Typeにcharset=Shift_JISつける必要はないよ。
そうですねぇ。とります!
ありがとうございます。

評価

0

なんか勘違いしてたかも。
StateBeanで全部管理してるんですね。
コネクション張りっぱなら問題無いかな。

HTMLを生成するサーブレットで、
1.コネクション取得
2.Blob取得
3.beanにBlobセット
4.コネクション解放
した後に、

画像をDLするサーブレットで
beanから取り出したBlob使ってBLOBの中身を取り出している
のかと思ってました。



改めてエラーメッセージを見てみると、
oracle.jdbc.driver.OracleResultSetImpl.getBlob
となっています。

1.4のソースを元にしているって事は、
oracle.jdbc.driver.OracleDriver
を指定していませんか?

これ、古いけど互換性の為に残っているドライバーで、
最近のは
oracle.jdbc.OracleDriver
です。
(こっちにすればgetBlobできそうな気もするけど、登録のロジックに影響あるかも)

そもそもJDBC4.0では、Class.forNameによるドライバーの読み込みは不要ですので、
指定しなければ自動でoracle.jdbc.OracleDriverを読み込んでくれると思います。

しかし、9iだからうまくいくか不安ですが。

評価

0

oracle.jdbc.driver.OracleDriverか
oracle.jdbc.OracleDriverかどちらを
指定しているかってのはどこを見れば
わかるのでしょうか?

基本的な確認ですが、今現在oracleのJDBCドライバは
D:\Tomcat\common\libに入っていて、特にどこでも
指定してませんが大丈夫ですよね?

なのでJDBC1.4から6に上げた際も、ただ単にjarを
入れ替えてtomcat再起動しただけですが…。

評価

0

TomcatでJDBCの設定をする方法は一意では無いので、
driverClassNameで*.xmlをgrepかけるか、

conf/server.xml
conf/context.xml
conf/エンジン名/ホスト名/context.xml
conf/エンジン名/ホスト名/アプリケーション名.xml
webapps/アプリケーション名/META-INF/context.xml

を調べてください。

jarの置き場所は合ってますよ。

評価

0

conf/server.xmlにdoriverNameの指定ありました!

<parameter>
 <name>driverClassName</name>
 <value>oracle.jdbc.driver.OracleDriver</value>
</parameter>


これを
<parameter>
 <name>driverClassName</name>
 <value>oracle.jdbc.OracleDriver</value>
</parameter>
に修正しました!


そしてtomcatを再起動して実行…


がしかし、エラー変わりません。
java.lang.AbstractMethodError: oracle.jdbc.driver.OracleResultSetImpl.getBlob(Ljava/lang/String;)Ljava/sql/Blob;


他にどこか指定しているところありますか?


評価

0

手元に9i環境は無いので、
11g(11.1.0.7.0)のojdbc6.jarを使って10gに繋いでみたけど、


ResultSet rs = stmt.executeQuery();
while (rs.next()) {
  Blob blob = rs.getBlob(1);
  InputStream is = blob.getBinaryStream();

って感じで普通に取得出来ちゃいました。

11g用のJDBCドライバーで、9iに接続は可能なはずなので、
ここからDLしてみたらどうでしょう?
http://www.oracle.com/technetwork/jp/database/enterprise-edition/jdbc-111060-097832-ja.html
※今使ってるドライバーは、捨てずにちゃんととっておいてね。

それでも駄目なら、java.sql.ResultSetのインタフェースからではなく、
oracle.driver.OracleResultSetのインタフェースからoracle.driver.BLOBを取得するのかな。

oracle.driver.OracleResultSetはjava.sql.ResultSetから派生してるので、

OracleResultSet ors = (OracleResultSet) rs;
BLOB blob = ors.getBLOB();
あるいは
BLOB blob = ((OracleResultSet)rs).getBLOB();


9i当時の流儀(JDBC3.0)的にはこっちなのかもしれない。

評価

0

なんのDBだったか忘れたけど、getBlob()の実装がおかしくて実行時エラーになるか
ら、getBytes()で取ったことがあったな。
入れる方は問題なかったと思うけど。

サイズが大きいとそういうわけにも行かないが…。

評価

0

D:\Tomcat\common\libを本当にちゃんと見ているのかと
疑ってドライバを消して実行してみましたが…

普通にデータベース読みにいってしましました。
という事はここを見ていないという事でしょうか?

ちなみに、今はoracleデータベースはサーバの方にあり
自分のローカルマシンでNetManegerで登録して見に
いっている状態です。
ソースはローカルにあります。

または、oracleのドライバを入れ替えた場合、apacheや
javaやマシンの再起動も必要ですか?

JDBCドライバが入れ替わってない可能性が高そうです。

評価

0

satomiさん、
http://www.oracle.com/technetwork/jp/database/enterprise-edition/jdbc-111060-097832-ja.html
ここのリンクは、ユーザ登録しなきゃダウンロードできませんか?

会社の情報などはきちんと書かなきゃならないでしょうか?

評価

0

古いjarが
$CATALINA_HOME
以下と、
念のため
$JAVA_HOME/lib/endorsed
に無いか確認してみて下さい。

WebAppだけじゃなく、
Tomcatの再起動も必要です。
DBのコネクションを管理しているのはTomcatで、
WebAppはそれを借りているだけなので。

PCやApacheの再起動はいりません。

あと、ドライバーのダウンロードには、ユーザー登録が必要になります。
正直に答えるべきかどうかですが、私は正直に申告しています。
心配でしたら、上司に相談してみたらどうですか?

評価

0

そこからなのか。
Windowsならスタートの直下に検索があるでしょ。
それでjarを検索して、いったん全部消してみればいい。

warで運用してるなら、その中に入ってる場合は検索に引っかからないから、そこも
注意点だな、

>oracleのドライバjdbc6に入れ替えてみましたが…
これはどこに入れたんだろう。

評価

0

$JAVA_HOME/lib/endorsedにはありませんでした。

また、前の方で説明していますがJDBCドライバの
入替後、初めはTomcatの再起動だけすれば大丈夫だと
思っていたのですが、それでも変わっていないよう
だったので、apacheやPCの再起動も試してみましたが
何も変わりませんでした。

そして、これも前の方で説明しましたが、jdbc6.jarはD:\Tomcat\common\libに置いています。
他の方に置き場所は合っていると言われたので大丈夫
かと思います。

検索もしてみましたが、ここ以外にドライバはありませんでした。

評価

0

実装クラス名が違うなら、xxx.getClass().getName()を表示してみるのも手だね。

同じクラス名が複数のjarに含まれている場合、標準クラスローダはjarのファイル名
でソートして上になるものをロードする。
仮に古いクラスがロードされてるなら、まず14がどこかにあるんだろう(6より14が
上になるので)。

例えば、Tomcatのworkの下にはwarの中身が全部展開されてる(展開する運用な
ら、だけど)。

評価

10

ResultSetを取得した後に、

Class clazz = resultSet.getClass();
URL location = target.getResource("/" + clazz.getName().replaceAll("\\.", "/") + ".class");
System.out.println(location.getPath());

とすると、標準出力ににjarのパスと、その中のクラスのパスが出力されるので、
古いjarの場所を探すのにどうぞ。

評価

0

少しに日にちがあいてしまいすみません。
上記の教えてもらったログをいれて表示してみたところ、
commons-dbcp-1.2.1.jarが出ました!

てっきり、ojdbc6.jarとかojdbc14.jarって出るかと思ったんですが…。

これは、古いのでしょうか?

評価

0

古いというか…それがどんなjarなのか、ぐぐってみるくらい
はしたのかな。

評価

0

はい!一応自分なりには調べました!

commons-dbcp-1.2.1.jarは、DBCP接続の際に必要なJDBCドライバのファイルの1つですよね?

つまり、上で教えてもらったログは、接続に使用しているjarを表示するものであって、oracleのドライバを表示するものではないという事でしょうか?

間違えてたらすみません。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。