sobre - java swing tutorial pdf español
La fila fue actualizada o eliminada por otra transacción(o la asignación de valores no guardados fue incorrecta) (13)
Tengo un proyecto de Java que se ejecuta en un servidor web. Siempre toco esta excepción.
Leí documentación y descubrí que el bloqueo pesimista (o optimista, pero he leído que pesimista es mejor) es la mejor manera de evitar esta excepción.
Pero no pude encontrar ningún ejemplo claro que explique cómo usarlo.
Mi método es como:
@Transactional
Public void test(Email email, String Subject){
getEmailById(String id);
email.setSubject(Subject);
updateEmail(email);
}
mientras:
-
Email
es una clase de hibernación (será una tabla en la base de datos) -
getEmailById(String id)
es una función que devuelve unemail
(este método no está anotado con@Transctional
) -
updateEmail(email)
: es un método que actualiza el correo electrónico.
Nota: uso hibernate para guardar, actualizar, etc. (ejemplo: session.getcurrentSession.save(email)
)
La excepción:
ERROR 2011-12-21 15:29:24,910 Could not synchronize database state with session [myScheduler-1]
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [email#21]
at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1792)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2435)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2335)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2635)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:115)
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:1027)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:365)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:137)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy130.generateEmail(Unknown Source)
at com.admtel.appserver.tasks.EmailSender.run(EmailNotificationSender.java:33)
at com.admtel.appserver.tasks.EmailSender$$FastClassByCGLIB$$ea0d4fc2.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:149)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:50)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at com.admtel.appserver.tasks.EmailNotificationSender$$EnhancerByCGLIB$$33eb7303.run(<generated>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.util.MethodInvoker.invoke(MethodInvoker.java:273)
at org.springframework.scheduling.support.MethodInvokingRunnable.run(MethodInvokingRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:51)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)
at java.util.concurrent.FutureTask$Sync.innerRunAndReset(FutureTask.java:317)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:150)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$101(ScheduledThreadPoolExecutor.java:98)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.runPeriodic(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:204)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
ERROR 2011-12-21 15:29:24,915 [ exception thrown < EmailNotificationSender.run() > exception message Object of class [Email] with identifier [211]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [Email#21] with params ] [myScheduler-1]
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Object of class [Email] with identifier [21]: optimistic locking failed; nested exception is
El bloqueo pesimista generalmente no se recomienda y es muy costoso en términos de rendimiento en el lado de la base de datos. El problema que ha mencionado (la parte del código) algunas cosas no son claras, como por ejemplo:
- Si su código está siendo accedido por múltiples hilos al mismo tiempo.
- ¿Cómo está creando el objeto de
session
(no estoy seguro si está usando Spring)?
Los objetos de sesión de Hibernate NO son seguros para subprocesos . Entonces, si hay varios subprocesos que acceden a la misma sesión e intentan actualizar la misma entidad de base de datos, su código puede potencialmente terminar en una situación de error como esta.
Entonces, lo que sucede aquí es que más de un subproceso intenta actualizar la misma entidad, un subproceso tiene éxito y cuando el siguiente subproceso va a confirmar los datos, ve que ya se ha modificado y termina tirando StaleObjectStateException
.
EDITAR :
Hay una forma de usar Bloqueo pesimista en Hibernate. Mira este enlace . Pero parece haber algún problema con este mecanismo. Sin embargo, me encontré publicando un error en Hibernate ( HHH-5275 ). El escenario mencionado en el error es el siguiente:
Dos hilos están leyendo el mismo registro de la base de datos; uno de esos hilos debería usar un bloqueo pesimista, bloqueando así el otro hilo. Pero ambos subprocesos pueden leer el registro de la base de datos causando que la prueba falle.
Esto está muy cerca de lo que estás enfrentando. Por favor, intente esto si esto no funciona, la única forma en que puedo pensar es usar consultas nativas de SQL donde se puede lograr un bloqueo pesimista en la base de datos postgres con la consulta SELECT FOR UPDATE
.
Esta excepción probablemente sea causada por un bloqueo optimista (o por un error en su código). Probablemente lo estés usando sin saberlo. Y su pseudocódigo (que debe ser reemplazado por un código real para poder diagnosticar el problema) es incorrecto. Hibernate guarda automáticamente todas las modificaciones hechas a las entidades adjuntas. Nunca deberías llamar a update, merge o saveOrUpdate en una entidad adjunta. Solo haz
Email email = session.get(emailId);
email.setSubject(subject);
No es necesario llamar a la actualización. Hibernate limpiará los cambios automáticamente antes de comprometer la transacción.
Este error ocurrió cuando estaba tratando de actualizar la misma fila de 2 sesiones diferentes. Actualicé un campo en un navegador mientras que un segundo estaba abierto y ya había almacenado el objeto original en su sesión. Cuando intenté actualizar desde esta segunda sesión "obsoleta", recibí el error de objeto obsoleto. Para corregir esto, vuelvo a ajustar mi objeto para que se actualice desde la base de datos antes de configurar el valor que se va a actualizar, y luego lo guardo de forma normal.
No parece que esté utilizando realmente el correo electrónico que recupera de la base de datos, sino una copia anterior que obtiene como parámetro. Lo que se está utilizando para el control de versiones en la fila ha cambiado entre cuando se recuperó la versión anterior y cuando se está realizando la actualización.
Es probable que desee que su código se parezca más a:
@Transactional
Public void test(String id, String subject){
Email email = getEmailById(id);
email.setSubject(subject);
updateEmail(email);
}
Para evitar StaleObjectStateException
, en su archivo hbm
escriba el siguiente código:
<timestamp name="lstUpdTstamp" column="LST_UPD_TSTAMP" source="db"/>
Sé que esta es una vieja pregunta, pero algunos de nosotros todavía estamos golpeándola y mirando el cielo vagando cómo. Aquí hay un tipo de problema que enfrenté,
Tenemos un administrador de colas que sondea los datos y los entrega a los manejadores para su procesamiento. Para evitar volver a buscar los mismos eventos, el gestor de colas bloquea el registro en la base de datos con un estado LOCKED.
void poll() {
record = dao.getLockedEntity();
queue(record);
}
este método no era transaccional, pero dao.getLockedEntity()
era transaccional con ''REQUIRED''.
Todo bien y en el camino, después de algunos meses en producción, falló con excepción de bloqueo optimista,
Después de una gran cantidad de depuración y comprobación de detalles, podríamos descubrir que alguien ha cambiado el código de esta manera,
@Transactional(propagation=Propagation.REQUIRED, readOnly=false)
void poll() {
record = dao.getLockedEntity();
queue(record);
}
Así que el registro se puso en cola incluso antes de que se confirmara la transacción en dao.getLockedEntity () (usa la misma transacción del método poll) y el objeto fue cambiado debajo por los manejadores (diferentes hilos) para el momento en que la transacción del método poll () comedido
Solucionamos el problema y se ve bien ahora.
Pensé en compartirlo, porque la excepción de bloqueo optimista puede ser confusa y difícil de depurar. Alguien podría beneficiarse de mi experiencia.
Saludos, Lyju
Solo en caso de que alguien revisara este hilo y tuviera el mismo problema que el mío ...
La fila fue actualizada o eliminada por otra transacción (o la asignación de valores no guardados fue incorrecta)
Estoy usando NHibernate, recibo el mismo error, durante la creación de un objeto ...
Estaba pasando la clave manualmente, y también especifiqué un generador de GUID en el mapeo, por lo que hibernate genera el mismo error exacto para mí, así que una vez que eliminé el GUID y dejé el campo vacío, todo salió bien.
esta respuesta puede no ser de ayuda, pero ayudará a alguien como yo, que es solo su hilo debido al mismo error
Tuve el mismo problema en mi proyecto de grails. El error fue que sobreescribí el método getter de un campo de colección. Esto siempre devuelve una nueva versión de la colección en otro hilo.
class Entity {
List collection
List getCollection() {
return collection.unique()
}
}
La solución fue cambiar el nombre del método getter:
class Entity {
List collection
List getUniqueCollection() {
return collection.unique()
}
}
Tuve el mismo problema y en mi caso el problema faltaba y / o el error es igual a la implementación en algunos tipos de campos en el objeto de entidad. En el momento de la confirmación, Hibernate verifica TODAS las entidades cargadas en la sesión para verificar si están sucias. Si alguna de las entidades está sucia, hibernate intenta persistir, sin importar que el objeto real que se solicita una operación de salvar no esté relacionado con las otras entidades.
La suciedad de la entidad se realiza al comparar cada propiedad del objeto dado (con sus métodos iguales) o UserType.equals
si la propiedad tiene un org.Hibernate.UserType
asociado.
Otra cosa que me sorprendió fue que, en mi transacción (usando la anotación Spring @Transactional
), estaba lidiando con una sola entidad. Hibernate se quejaba de alguna entidad aleatoria que no guarda relación con la entidad que se guarda. Lo que me di cuenta es que hay una transacción más externa que creamos a nivel de controlador REST, por lo que el alcance de la sesión es demasiado grande y, por lo tanto, todos los objetos que alguna vez se cargaron como parte del proceso de solicitud son revisados en busca de suciedad.
Espero que esto ayude a alguien, algún día.
Gracias Rags
Tuve el problema en mi proyecto.
Después de implementar el bloqueo optimista, obtuve la misma excepción. Mi error fue que no @Version
el setter del campo que se convirtió en @Version
. Cuando se llamaba al colocador en el espacio java, el valor del campo ya no coincidía con el generado por el DB. Así que, básicamente, los campos de versión ya no coincidían. En ese punto, cualquier modificación en la entidad resultó en:
org.hibernate.StaleObjectStateException: La fila fue actualizada o eliminada por otra transacción (o la asignación de valores no guardados era incorrecta)
Estoy usando H2 en la memoria DB e Hibernate.
Tuve este problema en una de mis aplicaciones, ahora, sé que este es un hilo viejo, pero esta es mi solución; Descubrí viendo los datos dentro del depurador que JVM en realidad no lo cargaba correctamente cuando Hibernate estaba tratando de actualizar la base de datos (que en realidad se hace en un hilo diferente), así que agregué la palabra clave "volátil" a cada campo de las entidades. Tiene algunos problemas de rendimiento para hacer eso, pero más que objetos pesados que se lanzan ...
Tuve la experiencia del mismo problema en el contexto diferente de mi proyecto y hay diferentes escenarios como
- object is accessed from various source like (server side and client)
- without any interval accessing the same object from a different place
En el primer caso
Cuando emito una llamada de servidor, antes de guardar el objeto, su única llamada de js y tratando de guardarla y otro lugar, recibí como, js call va dos, tres veces (creo que la cosa de enlace de llamadas causa el problema)
Lo resolví por
e.preventDefault()
El segundo caso,
object.lock()
compruebe si el objeto existe o no en DB, si existe consiga el objeto y actualícelo:
if (getEntityManager().contains(instance)) {
getEntityManager().refresh(instance);
return instance;
}
si falla la condición if anterior ... encuentre el Objeto con Id en DB, realice la operación que necesita, en este caso exactamente se reflejarán los cambios.
if (....) {
} else if (null != identity) {
E dbInstance = (E) getEntityManager().find(instance.getClass(), identity);
return dbInstance;
}