javaのjava.sql.PrepareStatementのexecuteUpdateメソッドを使用しOracleにデータ登録した際、

設定された値とは異なる値が登録されるという事象が発生しました。

事象は極まれにのみ発生し、ソースコードを追ってみても問題点が見つかりません。

事象を簡単にまとめると、
1.コネクション接続

2.SQL文生成
  String sql =
   "INSERT INTO TEST_TABLE(ID, NAME, UPDATE_TIME) VALUES ( ? , ? , SYSDATE )";

3.SQL実行
PrepareStatementに、setObjectメソッドで、
  値を設定する。
   一つ目のデータ:"1"(String型)
   二つ目のデータ:"3"(String型)

  その後、PrepareStatementのexecuteUpdateメソッドで処理を実行する。

上記処理で稀にTEST_TABLE.NAMEに「3」ではなく「0」が入っているものがあった。

Oracleバージョン:Oracle10g(10.1.0.2.0)
javaバージョン:1.4.2_06

該当テーブル仕様
-------------------------------------
カラム名 型 長さ NOT NULL
-------------------------------------
ID NUMBER (8,0) NOT NULL
MEMBER_NO NUMBER (4,0)
UPDATE_TIME DATE

より詳細な情報を追記します。

回答の条件
  • 1人5回まで
  • 登録:2006/11/02 14:59:06
  • 終了:2006/11/09 15:00:02

回答(3件)

id:kato-s No.1

kato-s回答回数91ベストアンサー獲得回数32006/11/02 17:08:42

ポイント27pt

回答ではないのですが、


String型であれば、

setObjectでなく、setStringを使用するようにする。


"3"はリテラルですか?であれば、SQL分に埋めてしまう。


"3"の部分が変数の場合は、本当に変数に"0"が入っていた

可能性が無いかログを出力して見る。


※ソースは問題ないとのことですが、

可能な部分を良ければ公開してみてください。

id:f_kennedy

>"3"はリテラルですか?であれば、SQL分に埋めてしまう。

変数です。

>"3"の部分が変数の場合は、本当に変数に"0"が入っていた

>可能性が無いかログを出力して見る。

吐かれたログを確認しましたが、"3"がはいっていました。

(SQL実行後の、値を入れたListの値を出力したログで確認。

#private int executeSql

#logger.info(list);

2006/11/06 20:24:04
id:F57PB No.2

F57PB回答回数86ベストアンサー獲得回数02006/11/02 22:56:41

ポイント27pt

ざっとソースを拝見しましたが、以下気になりました。


・コネクション取得

 DataSourceと、DriverManager両方からConnectionを取得

 していますが、実際そうなのでしょうか?


・トランザクション制御

 DBAccessで、Connection#setAutoCommit(false)していますが、

 どこでコミットしていますか?

 トランザクション境界を明確に意識したほうが良いと思います。


・スレッドセーフ性

 DBAccessクラスを含め、スレッドセーフ性は確保されていますか?

 一般的に、ConnectionやStatement、ResultSetは

 インスタンスフィールドに格納しない方が良いです。


・クローズ

 Connection、Statement、ResultSetのクローズは確実に行なわれるよう、

 finally句で行なうのがセオリーです。


・テーブル仕様に、"NAME"と言うカラムがないようですが。。。

 掲載して頂いているソースやテーブル仕様は正しいですか?


あと、P6Spy(www.p6spy.com/)と言うSQLをログ出力するライブラリがあります。

現象を再現できるのであれば、P6Spyを使ってどのようなSQLが実行されている

のかを確認してはどうでしょうか?


この手の問題は、私の経験上、十中八九勘違いか思い込みです。

一歩引いて、ゆっくり見直すこともお勧めします。

id:f_kennedy

>・コネクション取得

Connection con =

java.sql.DriverManager.getConnection("jdbc:oracle:thin:@「接続先アドレス」:「接続文字列」",「ユーザー名」,「パスワード」);

は、テスト用の文の残りで実際はコメント化されてます。

提示ソースに至らないところがありました。申し訳ありません。

>・トランザクション制御

>・スレッドセーフ性

>・クローズ

上記の理由で変なデータが入ってしまう可能性はありますでしょうか?

>・テーブル仕様に、"NAME"と言うカラムがないようですが。。。

ソースの書き写しを間違ってしまいました。

実際は、

×String sql = "INSERT INTO TEST_TABLE(ID, NAME, UPDATE_TIME) VALUES ( ? , ? , SYSDATE)";

○String sql = "INSERT INTO TEST_TABLE(ID, MEMBER_NO, UPDATE_TIME) VALUES ( ? , ? , SYSDATE)";

です。

2006/11/06 20:28:21
id:samasuya No.3

samasuya回答回数315ベストアンサー獲得回数112006/11/03 15:20:27

ポイント26pt

テーブル定義がSQL文とあってないのですが、

Stringで設定した値が空白、NULLとかなんじゃないでしょうか?

  • id:f_kennedy
    /******************************************************************************
    * SQL実行
    *****************************************************************************/
    // Context生成
    Context context = new InitialContext();

    // DataSourceを取得する
    DataSource ds = (DataSource)context.lookup(「DB設定判別文字列」);

    // コネクションを取得する
    Connection con = ds.getConnection();
    Connection con =
    java.sql.DriverManager.getConnection("jdbc:oracle:thin:@「接続先アドレス」:「接続文字列」",「ユーザー名」,「パスワード」);

    // SQL文生成
    String sql = "INSERT INTO TEST_TABLE(ID, NAME, UPDATE_TIME) VALUES ( ? , ? , SYSDATE)";

    //テーブルカラム定義は以下のものとする
    //-----------------------------------
    //カラム名 型 長さ NOT NULL
    //ID NUMBER (8,0) NOT NULL
    //MEMBER_NO NUMBER (4,0)
    //UPDATE_TIME DATE
    //-----------------------------------

    // 値挿入リスト生成
    List list = new ArrayList();
    list.add("1");
    list.add("3");

    // SQL実行処理へ
    executeSql(con, sql, list);
    ~~~~~~~~~~

    /******************************************************************************
    * executeSqlメソッド
    *****************************************************************************/
    private int executeSql(Connection con, String sql, List list) throws Exception
    {
    int result = 0;
    try
    {
    DBAccess db = new DBAccess(con);
    result = db.executeUpdate(sql, list);
    logger.info(list);
    logger.info("処理結果 : " + result);
    }catch(Exception e){
    logger.error("エラーが発生しました",e);
    throw e;
    }
    return result;
    }
  • id:f_kennedy

    /******************************************************************************
    * DBAccessクラス
    *****************************************************************************/
    private Connection con = null;
    private PrepareStatement stmt = null;
    private ResultSet rs = null;

    // コンストラクタ
    public DBAccess(Connection con){
    try{
    this.con = con;
    con.setAutoCommit(false);
    }catch(Exception ex){
    }

    }


    // 登録実行処理メソッド
    public int executeUpdate(String sql, List params) throws Exception{
    // Statementオブジェクトを生成する
    stmt = con.prepareStatement(sql);
    for(int i = 0; i < params.size(); i++){
    // パラメータをセットする
    Object obj = params.get(i);
    if(obj instanceof Date){
    stmt.setTimestamp(i+1, new Timestamp(((Date)obj).getTime()));
    }else if(obj instanceof BigDecimal){
    stmt.setBigDecimal(i+1, (BigDecimal)obj);
    }else if(obj == null){
    stmt.setNull(i+1, Types.NUMERIC);
    }else{
    stmt.setObject(i+1, obj); ←String型なのでここで値を詰める。
    }
    }
    int ret =stmt.executeUpdate();

    // DBをクローズする。
    close();
    return ret;
    }
  • id:salic
    何かエラーは吐き出されていませんか?

    データが1つの場合でも発生する事が有りますか?

    予め初期値としてデータベースに9とか入れておき、updateされていなければ
    9のままだし、0になれば何らかの原因で0が送られたチェックはしましたか?

  • id:yuki_n
    カラム名が違うのは、突っ込んではいけないところ?
     MEMBER_NO ≠ NAME

    参照してるテーブルとか、接続先サーバとか、実は参照してるソースとか、合ってますでしょうか?


    明後日の方向の突込みなら失礼。 m(_ _)m
  • id:f_kennedy
    >>salicさん
    >何かエラーは吐き出されていませんか?
    エラーは、吐かれていません。

    >データが1つの場合でも発生する事が有りますか?
    >予め初期値としてデータベースに9とか入れておき、updateされていなければ
    >9のままだし、0になれば何らかの原因で0が送られたチェックはしましたか?

    発生した時は、データは一つではありません。
    現象自体が、一回きり発生したのみで、
    その後実行すると、ちゃんとデータは入ります

    >>yuki_nさん
    >カラム名が違うのは、突っ込んではいけないところ?
    ソースの書き写しを間違ってしまいました。
    カラム名については問題ありません。



    今のところコードとして誤りが見つけられないので
    javaもしくはoracleのバグではないかとも考えているのですが・・・
    そちらの方面でも何か情報などございましたら
    どうぞよろしくお願いいたします。
  • id:F57PB
    >>・トランザクション制御
    >>・スレッドセーフ性
    >>・クローズ
    > 上記の理由で変なデータが入ってしまう可能性はありますでしょうか?
    あります。
    特にスレッドセーフ性が保障されていないプログラムがマルチスレッドに
    動作すればどのような値が入るか予測・再現は困難になります。
    Servletを使ったWebアプリケーションなんかはマルチスレッドに動作するので
    プログラムはスレッドセーフ性に気を配らないといけないですね。

    > 現象自体が、一回きり発生したのみで、
    > その後実行すると、ちゃんとデータは入ります
    現象が1回発生しただけで、その後は再現していないということでしょうか?
    その時、ソースコードの状態やDB環境がどうであったのかは確認できますか?

    再現しないのであれば、しばらく注意してウォッチすると言う事で、
    一端保留しても良いのではないかと思いますが。。。

    > javaもしくはoracleのバグではないかとも考えているのですが・・・
    私の経験上、JavaやDBMS、JDBCドライバのバグではないかと疑って
    これらのバグであってことは1度もありません。
    世界中で何千、何万と言う使用実績があるミドルウェアでバグが発見されることは、
    通常の開発をしている限り皆無に近いと思ったほうが良いと思います。
  • id:f_kennedy
    皆様、ご回答ありがとうございました。
    しばらく使わないうちにシステムが変わってしまったようで
    ポイントが自動割り振りされてしまっていました。
    失礼しました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません