Hi,
I am running a test to make sure that my system can recover from a persistence exception. My test scenario is:
I persist an entity with a mandatory field set to null (in order for the save to fail)
I catch the exception being thrown
I populate the missing field
I re-persist the entity
I expect the entity to be saved, but I am having an exception.
Code:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:92)
(please see below for the complete stack trace)
In terms of Transaction management, here it what happens
On first persistence attempt: The transaction is created and rolled back when the error occurs while persisting the null value. Then, the session is cleared.
The object is modified in order not to have any null values and on the second attempt, another transaction is created for saving the updated entity. The transaction rolls back because an exception is thrown by the Batcher.
I have read elswhere that this might be due to batching and that I should try changing the batch update size. I turned it on and off and it makes no difference both the NonBatchingBatcher and the BatchingBatcher throw the same type of exception:
Code:
org.hibernate.HibernateException: Unexpected row count: 0 expected: 1
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:32)
Hibernate version: 3.0.2
Mapping documents:The entity that is being persisted in the test.
Code:
<class name="SecurityRoleImpl" table="security_role" lazy="true">
<id name="id" column="id" type="java.lang.Long">
<generator class="hilo"/>
</id>
<property name="name" column="role_name" type="java.lang.String" not-null="true" />
<property name="creationDate" column="created" type="java.util.Date" not-null="true" access="field" />
<property name="lastModificationDate" column="modified" type="java.util.Date" not-null="true" access="field" />
<many-to-one unique="true" name="domain" column="domain_id" class="SecurityDomainImpl" access="field"></many-to-one>
<set name="assignedAcls" table="security_role_to_acl" lazy="true" inverse="true" cascade="save-update" access="field">
<key column="role_id"/>
<many-to-many class="SecurityACLImpl" column="acl_id"/>
</set>
<filter name="FilterSecurityObjectBasedOnDomainId" condition="domain_id=:domainId"/>
</class>
Code between sessionFactory.openSession() and session.close():Please read notes below for variable description.
Code:
private Object processWithTransaction(StandaloneServiceManagerCommand cmd) throws ServiceException, ExistingSecurityObjectException
{
boolean exceptionThrown = false;
Transaction trx = strategy.getSession().beginTransaction();
try{
return cmd.execute(entitlementManagementServices);
}
--- NOTE ---
--- A couple of specific exceptions are catched here... ---
catch(Exception e){
exceptionThrown = true;
throw new ServiceException("An unknow exception was thrown while executing a persistence command.", e);
}
finally{
if(exceptionThrown){
rollbackTransaction(trx);
}else{
try{
commitTransaction(trx);
}catch(Exception e){
rollbackTransaction(trx);
}
}
}
}
NOTE:
strategy is an object that is injected in every object that requires a session. It synchronizes the session to the current thread.
cmd is a command executed by the transaction manager... The command that is executed for a save is roughly: session.save(entity);
Full stack trace of any exception that occurs:Code:
org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
at org.hibernate.jdbc.BatchingBatcher.checkRowCount(BatchingBatcher.java:92)
at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:78)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:172)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:137)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:678)
at com.db.eps.common.persistence.hibernate.support.AbstractHibernateDAO.process(AbstractHibernateDAO.java:47)
at com.db.eps.common.persistence.hibernate.support.AbstractHibernateDAO.saveOrUpdate(AbstractHibernateDAO.java:109)
at com.db.eps.common.entitlement.service.persistence.DAO.impl.HibernateSecurityObjectDAO.save(HibernateSecurityObjectDAO.java:44)
at com.db.eps.common.entitlement.service.facade.pojo.EntitlementManagementServicesImpl.saveRole(EntitlementManagementServicesImpl.java:152)
at com.db.eps.common.entitlement.service.facade.pojo.StandaloneEntitlementManagementServiceImpl$3.execute(StandaloneEntitlementManagementServiceImpl.java:60)
at com.db.eps.common.entitlement.service.facade.pojo.StandaloneEntitlementManagementServiceImpl.processWithTransaction(StandaloneEntitlementManagementServiceImpl.java:365)
at com.db.eps.common.entitlement.service.facade.pojo.StandaloneEntitlementManagementServiceImpl.saveRole(StandaloneEntitlementManagementServiceImpl.java:58)
at com.db.eps.common.entitlement.service.facade.pojo.StandaloneEntitlementManagementServiceImplTest.testStandaloneEntitlementServiceCanRetrieveDataAfterAWriteFailureForNotNullValue(StandaloneEntitlementManagementServiceImplTest.java:123)
Name and version of the database you are using:
Sybase
The generated SQL (show_sql=true):
Quote:
update security_role set role_name=?, created=?, modified=?, domain_id=? where id=?
Thanks for your help,
Vincent Giguere