2009-12-24 15 views
9

Estoy usando Hibernate, tratando de simular 2 actualizaciones concurrentes a la misma fila en la base de datos.hibernate Excedido el tiempo de espera de espera de bloqueo;

Editar: Moví em1.getTransaction(). Me comprometo a estar justo después de em1.flush(); No recibo ninguna StaleObjectException, las dos transacciones se han confirmado correctamente.

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()); 

obteniendo la siguiente excepción en em2.flush(). ¿Por qué?

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 

Respuesta

20

Bueno, que está tratando de entrar en un callejón sin salida y que está teniendo éxito :-)

  1. comienza Transaction1, actualizaciones (y cerraduras) fila con su entidad.
  2. Transaction2 intenta hacer lo mismo pero no puede porque la fila todavía está bloqueada. Por lo que espera (y espera, y espera) hasta que se supere el tiempo de espera

la vida real simulación tendría gestor de la entidad 1 y 2, más actualizaciones/operaciones apropiadas en hilos separados. De esta manera usted tendría:

  1. La transacción 1 inicia, actualiza (y bloquea) la fila con su entidad.
  2. Transaction2 intenta hacer lo mismo pero no puede porque la fila todavía está bloqueada. Por lo que espera (y espera, y espera) ...
  3. Mientras tanto Transaction1 está comprometido y bloqueo se libera
  4. Transaction2 puede ahora proceder

Tenga en cuenta que en ese punto (# 4 arriba) que había sobrescribir los cambios realizados por Transaction1. Hibernate puede usar optimistic locking y pessimistic locking para evitar que eso suceda.

actualización (basado en comentario):

Si se versiona la entidad, Transaction2 (# 4 arriba) se producirá un error. Sin embargo, su código tal como se publicó no llega a ese punto porque Transaction2 no puede obtener el bloqueo como se explicó anteriormente. Si desea probar específicamente que el control de versiones optimista está trabajando se puede hacer lo siguiente:

  1. Obtener em1, se inicia la transacción, obtener su entidad, cometer transacción, cerca em1.
  2. Obtenga em2, inicie la transacción, obtenga su entidad, actualice su entidad, confirme la transacción, cierre em2.
  3. Obtenga em3, inicie la transacción, intente actualizar la entidad que ha cargado en el paso 1: la prueba debería fallar aquí.
+0

Estoy tratando de escribir un caso de prueba para ver si funciona el bloqueo optimista, el objeto UserCuenta en cuestión está utilizando la versión, consulte esta pregunta para obtener más información http://stackoverflow.com/questions/1938671/concurrency- in-hibernate, en este caso con dos hilos ¿transacción2 obtendría staledObjectException para detectar un cambio en los datos subyacentes? – user217631

+0

@unknown Sí, lo haría. – KLE

+0

He actualizado mi respuesta anterior: usar dos hilos no es una buena manera de probar ** específicamente ** bloqueo optimista (debido a ser impredecible) – ChssPly76

Cuestiones relacionadas