私はHibernateを使用しており、データベースの同じ行に対する2つの同時更新をシミュレートしようとしています。
編集:em1.getTransaction()。commitをem1.flush();の直後に移動しました。 2つのトランザクションが正常にコミットされたStaleObjectExceptionが発生しません。
_Session em1=Manager.sessionFactory.openSession();
Session em2=Manager.sessionFactory.openSession();
em1.getTransaction().begin();
em2.getTransaction().begin();
UserAccount c1 = (UserAccount)em1.get( UserAccount.class, "root" );
UserAccount c2 = (UserAccount)em2.get( UserAccount.class, "root" );
c1.setBalance( c1.getBalance() -1 );
em1.flush();
System.out.println("balance1 is "+c2.getBalance());
c2.setBalance( c2.getBalance() -1 );
em2.flush(); // fail
em1.getTransaction().commit();
em2.getTransaction().commit();
System.out.println("balance2 is "+c2.getBalance());
_
em2.flush()
で次の例外が発生します。どうして?
_2009-12-23 21:48:37,648 WARN JDBCExceptionReporter:100 - SQL Error: 1205, SQLState: 41000
2009-12-23 21:48:37,649 ERROR JDBCExceptionReporter:101 - Lock wait timeout exceeded; try restarting transaction
2009-12-23 21:48:37,650 ERROR AbstractFlushingEventListener:324 - Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.Java:126)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.Java:114)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.Java:66)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.Java:275)
at org.hibernate.persister.entity.AbstractEntityPersister.processGeneratedProperties(AbstractEntityPersister.Java:3702)
at org.hibernate.persister.entity.AbstractEntityPersister.processUpdateGeneratedProperties(AbstractEntityPersister.Java:3691)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.Java:147)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.Java:279)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.Java:263)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.Java:168)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.Java:321)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.Java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.Java:1028)
at com.ch.whoisserver.test.StressTest.main(StressTest.Java:54)
Caused by: Java.sql.BatchUpdateException: Lock wait timeout exceeded; try restarting transaction
at com.mysql.jdbc.PreparedStatement.executeBatchSerially(PreparedStatement.Java:1213)
at com.mysql.jdbc.PreparedStatement.executeBatch(PreparedStatement.Java:912)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.Java:70)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.Java:268)
... 10 more
_
さて、あなたはデッドロックに陥ろうとしていて、成功しています:-)
実際のシミュレーションでは、1番目と2番目のエンティティマネージャーに加えて、個別のスレッドで適切な更新/トランザクションが行われます。そうすれば、次のようになります。
その時点(上記の#4)で、Transaction1によって行われた変更を上書きすることに注意してください。 Hibernateは、 楽観的ロック および 悲観的ロック を使用して、それが発生しないようにすることができます。
更新(コメントに基づく):
エンティティがバージョン管理されている場合、Transaction2(上記の#4)は失敗します。ただし、Transaction2は上記で説明したようにロックを取得できないため、投稿されたコードはその時点に到達しません。楽観的なバージョン管理が機能していることを具体的にテストする場合は、次の操作を実行できます。