Envers still tries to audit after transaction rolled back
gingming Jun 21, 2012 1:06 AMHi,
I'm using Envers 3.6.0 in Seam 2.2.2.Final under JBoss 6. I've encountered the following error that I couldn't solve. The following is an example of the code that I ran:
public class ServiceProvider {
@Column(name = "code", unique = true, nullable = false, length = 128)
public String getCode() {
return this.code;
}
@OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.REMOVE, CascadeType.PERSIST}, mappedBy="serviceProvider")
public Set<ServiceProviderProperty> getServiceProviderProperties() {
return this.serviceProviderProperties;
}
.
.
.
}
public class ServiceProviderProperty {
@Column(name = "code", unique=true, nullable = false, length = 128)
public String getCode() {
return this.code;
}
}
Another class:
@Transactional
public void persist(ServiceProvider sp) {
serviceProviderDao.persist(sp);
}
The ServiceProvider and ServiceProviderProperty database tables have unique constraint on the code.
Scenario 1:
If I insert service provider with duplicate code, exception is thrown, the transaction is rolled back and envers is not invoked.
Scenario 2:
If I insert service provider with service provider properties with duplicate code (i.e. inserted via cascading releationship), exception is thrown, the transaction is rolled back BUT envers is still invoked. Because my Audit Entity contains some required fields that is not populated, it ended up in a infinite loop, e.g.
14:58:58,912 ERROR [org.jboss.seam.exception.Exceptions] handled and logged exception: java.lang.IllegalStateException: Could not commit transaction
at org.jboss.seam.jsf.SeamPhaseListener.commitOrRollback(SeamPhaseListener.java:624) [:2.2.2.Final]
at org.jboss.seam.jsf.SeamPhaseListener.commitOrRollback(SeamPhaseListener.java:603) [:2.2.2.Final]Caused by: javax.transaction.RollbackException: ARJUNA-16053 Could not commit transaction.
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1170) [:6.0.0.Final]Caused by: org.hibernate.AssertionFailure: Unable to perform beforeTransactionCompletion callback
at org.hibernate.engine.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:549) [:3.6.0.Final]
at org.hibernate.engine.ActionQueue.beforeTransactionCompletion(ActionQueue.java:216) [:3.6.0.Final]Caused by: org.hibernate.validator.InvalidStateException: validation failed for: com.test.ConfigurationChangeRecord
at org.hibernate.validator.event.ValidateEventListener.validate(ValidateEventListener.java:148) [:4.0.2.GA]
at org.hibernate.validator.event.ValidateEventListener.onPreInsert(ValidateEventListener.java:172) [:4.0.2.GA]
at org.hibernate.action.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:160) [:3.6.0.Final]
at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:65) [:3.6.0.Final]
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273) [:3.6.0.Final]
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:320) [:3.6.0.Final]
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203) [:3.6.0.Final]
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129) [:3.6.0.Final]
at org.hibernate.ejb.event.EJB3SaveEventListener.saveWithGeneratedId(EJB3SaveEventListener.java:62) [:3.6.0.Final]
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210) [:3.6.0.Final]
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56) [:3.6.0.Final]
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195) [:3.6.0.Final]
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50) [:3.6.0.Final]
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93) [:3.6.0.Final]
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:713) [:3.6.0.Final]
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:701) [:3.6.0.Final]
at org.hibernate.envers.revisioninfo.DefaultRevisionInfoGenerator.saveRevisionData(DefaultRevisionInfoGenerator.java:71) [:3.6.0.Final]
at org.hibernate.envers.synchronization.AuditProcess.getCurrentRevisionData(AuditProcess.java:126) [:3.6.0.Final]
at org.hibernate.envers.synchronization.AuditProcess.executeInSession(AuditProcess.java:104) [:3.6.0.Final]
at org.hibernate.envers.synchronization.AuditProcess.doBeforeTransactionCompletion(AuditProcess.java:143) [:3.6.0.Final]
at org.hibernate.engine.ActionQueue$BeforeTransactionCompletionProcessQueue.beforeTransactionCompletion(ActionQueue.java:543) [:3.6.0.Final]
... 72 more
My question is, why is envers still got invoked in Scenario 2 even if the transaction is rolled back? How can I prevent that from happening?
I've tried to set the service provider property collection to null, persist the service provider first and then persist the properties using serviceProviderDao in the same transaction, and the same problem occurred. So it has nothing to do with the cascading persist. Any idea?