1
0

More than 1 year has passed since last update.

Hibernate3によるupdate時にStaleObjectStateExceptionが発生するようになった原因

Posted at

事象

コードは何も変えていないのに
hibernate3 で insert してから update すると突然下記のエラーが発生するようになりました。

org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)

環境は下記のいずれの組み合わせでも発生。
Java6, Java8
MySQL 5.5, MySQL 5.6, MySQL 5.7

そんな古い Version を使うなよ。という突っ込みはさておき原因を調べてみました。

原因

数年ぶりに MySQL Connector/J を最新に上げたことにより発生するようになっていました。

エラーになる流れとしては

  1. hibernate3 で insert をする。
    このとき、Java ではミリ秒を保持し、DB ではミリ秒は四捨五入される。

  2. その後、同じ entity で update する。
    Java ではミリ秒を保持しており、update 時にミリ秒も評価するので楽観ロックエラーになる。

どの version からエラーになるのか調べると
MySQL Connector/J 5.1.23 からエラーになるようです。
Release Notes を見てみました。

この変更が影響してそうな気がします

The nativeSQL() method would always truncate fractional seconds rather than preserving the fractional part in the output string. Now Connector/J checks the server version: it preserves the fractional part for MySQL 5.6.4 and greater, and truncates the fractional part for older versions. (Bug #14748459, Bug #60598)

日本語訳

nativeSQL() メソッドは、出力文字列の端数部分を保存するのではなく、常に端数秒を切り捨てていました。現在 Connector/J はサーバ バージョンをチェックし、MySQL 5.6.4 以降では端数部分を保存し、それ以前のバージョンでは端数部分を切り捨てます。

MySQL Connector/J 5.1.22 以前だと update 時にはミリ秒以下を評価の対象にしていなかったためエラーにならなかったと思われます。(nativeSQL()が何かはよくわかってません・・)

ただ、謎なのは動作環境によってエラーになったりならなかったりしていて、 MySQL やサーバの version や設定の違いが関係しているのかもしれませんが、時間と気力の制約上これ以上の深追いはやめておきます。。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0