[hibernate-issues] [Hibernate-JIRA] Commented: (ANN-596) ManyToMany with CascadeType ALL => StaleStateException

Christian Bauer (JIRA) noreply at atlassian.com
Thu Apr 19 05:22:04 EDT 2007


    [ http://opensource.atlassian.com/projects/hibernate/browse/ANN-596?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#action_26760 ] 

Christian Bauer commented on ANN-596:
-------------------------------------

Attach a runable standalone testcase if you want anyone to look at this, no Spring. Ideally modify an existing test case in Hibernate to reproduce the problem.


> ManyToMany with CascadeType ALL => StaleStateException
> ------------------------------------------------------
>
>                 Key: ANN-596
>                 URL: http://opensource.atlassian.com/projects/hibernate/browse/ANN-596
>             Project: Hibernate Annotations
>          Issue Type: Bug
>    Affects Versions: 3.2.0.cr2, 3.3.0.ga
>         Environment: Hibernate 3.2.0cr2 with Annotations 3.2.0.cr2; and Hibernate 3.2 with Annotations 3.3.0ga.
> Spring 2.0.4
> HSQL 1.8.0 and MySQL 5.0
>            Reporter: Steven Morrison
>            Priority: Minor
>         Attachments: HibernateManyToManyMappingError.zip
>
>
> Originally posted as bug to forums (http://forum.hibernate.org/posting.php?mode=editpost&p=2345494), but received no response.
> [b]Description:[/b] Hibernate appears to be end up in an inconsistent state when working with a many-to-many mapping using cascade = ALL.
> The test below shows Hibernate persisting a Root (with a Value mapped as many-to-many), then later deleting the Root (which also deletes the Value). When attempting to delete the Value, hibernate's "listing entities" logging suggests that the Value instance is still persisted, but the Root deletion has removed this. StaleStateException is then thrown.
> [b]Hibernate version:[/b] 3.2
> [b]Annotated classes:[/b]
> [code]
> @Entity
> public class RootOfManyToMany
> {
>     @Id
>     @GeneratedValue( generator = "hibseq" )
>     @GenericGenerator( name = "hibseq", strategy = "hilo" )
>     public Long                            id;
>     @ManyToMany( fetch = FetchType.EAGER, cascade = javax.persistence.CascadeType.ALL )
>     @JoinTable(
>          name = "MAPPING_TABLE",
>          joinColumns = {@JoinColumn(name = "ROOT_ID")},
>          inverseJoinColumns = {@JoinColumn(name = "VALUE_ID")}
>     )
>     public List< ValueOfManyToMany > values;
> }
> [/code]
> [code]
> @Entity
> public class ValueOfManyToMany
> {
>     @Id
>     @GeneratedValue( generator="hibseq" )
>     @GenericGenerator( name="hibseq", strategy="hilo" )
>     public Long id;
> }
> [/code]
> Spring config:
> [code]
> <?xml version="1.0" encoding="UTF-8"?>
> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
> 		  "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
> <beans>
> 	<!-- HSQL for local tests -->
> 	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
>       	<property name="driverClassName">        <value>org.hsqldb.jdbcDriver</value> </property>
>        	<property name="username">               <value>sa</value>                    </property>
>       	<property name="password">               <value></value>                      </property>
>       	<property name="maxActive">              <value>2</value>                     </property>
>       	<property name="poolPreparedStatements"> <value>false</value>                 </property>      	      	
>       	<property name="url" value="jdbc:hsqldb:mem:test"/>
>      </bean>
> 	
> 	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
> 	  <property name="dataSource" ref="dataSource"/>
> 	  <property name="hibernateProperties">
> 	    <props>
> 	      <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
> 		  <prop key="hibernate.show.sql">true</prop>
>  		  <prop key="hibernate.hbm2ddl.auto">update</prop>
> 	    </props>
> 	  </property>
> 	  
> 	  <property name="annotatedClasses">
> 	  	<list>
> 	  	  <value>RootOfManyToMany</value>
> 	  	  <value>ValueOfManyToMany</value>
> 	  	</list>
> 	  </property>
> 	</bean>
> 	<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
> 	  <constructor-arg ref="sessionFactory"/>
> 	</bean>
> 	
> </beans>
> [/code]
> [b]Code between sessionFactory.openSession() and session.close():[/b]
> [code]
>     public void test_manyToManyRelationship() throws Exception 
>     {
>         RootOfManyToMany rootOfManyToMany = new RootOfManyToMany();
>         rootOfManyToMany.values = new ArrayList<ValueOfManyToMany>();
>         rootOfManyToMany.values.add( new ValueOfManyToMany() );
>         
>         assertEquals( 0, hibernateTemplate.find( "from RootOfManyToMany" ).size() );
>         
>         Long id = (Long)hibernateTemplate.save( rootOfManyToMany );
>         hibernateTemplate.flush();
>         
>         assertEquals( 1, hibernateTemplate.find( "from RootOfManyToMany" ).size() );
>         
>         RootOfManyToMany root = (RootOfManyToMany)hibernateTemplate.get( RootOfManyToMany.class, id );
>         ValueOfManyToMany value = root.values.get( 0 );
>         
>         hibernateTemplate.delete( root );
>         hibernateTemplate.flush();
>         
>         hibernateTemplate.delete( value ); //Exception thrown here
>     }
> [/code]
> [b]Full stack trace of any exception that occurs:[/b]
> org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update: 0 actual row count: 0 expected: 1
> Caused by: 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:93)
> 	at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
> 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
> 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
> 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
> 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
> 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:993)
> 	at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:388)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:363)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:774)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:770)
> 	at RootOfManyToManyTest.test_manyToManyRelationship(RootOfManyToManyTest.java:40)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> 	at java.lang.reflect.Method.invoke(Unknown Source)
> 	at junit.framework.TestCase.runTest(TestCase.java:154)
> 	at junit.framework.TestCase.runBare(TestCase.java:127)
> 	at junit.framework.TestResult$1.protect(TestResult.java:106)
> 	at junit.framework.TestResult.runProtected(TestResult.java:124)
> 	at junit.framework.TestResult.run(TestResult.java:109)
> 	at junit.framework.TestCase.run(TestCase.java:118)
> 	at junit.framework.TestSuite.runTest(TestSuite.java:208)
> 	at junit.framework.TestSuite.run(TestSuite.java:203)
> 	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
> 	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
> [b]Name and version of the database you are using:[/b]
> HSQL 1.8.0.2
> [b]The generated SQL (notable log entries with <returned values>:[/b]
> [code]
> select rootofmany0_.id as id4_ from ROOT_OF_MANY_TO_MANY rootofmany0_
> <returns empty list>
> insert into ROOT_OF_MANY_TO_MANY (id) values (?)
> binding '1' to parameter: 1
> insert into VALUE_OF_MANY_TO_MANY (id) values (?)
> binding '32768' to parameter: 1
> insert into MAPPING_TABLE (ROOT_ID, VALUE_ID) values (?, ?)
> binding '1' to parameter: 1
> binding '32768' to parameter: 2
> listing entities:
> RootOfManyToMany{values=[ValueOfManyToMany#32768], id=1}
> ValueOfManyToMany{id=32768}
> <returns id=1>
> select rootofmany0_.id as id4_ from ROOT_OF_MANY_TO_MANY rootofmany0_
> result set row: 0
> returning '1' as column: id4_
> select values0_.ROOT_ID as ROOT1_1_, values0_.VALUE_ID as VALUE2_1_, valueofman1_.id as id5_0_ from MAPPING_TABLE values0_ left outer join VALUE_OF_MANY_TO_MANY valueofman1_ on values0_.VALUE_ID=valueofman1_.id where values0_.ROOT_ID=?
> returning '1' as column: ROOT1_1_
> returning '32768' as column: VALUE2_1_
> done processing result set (1 rows)
> <returns singleton list>
> select rootofmany0_.id as id4_1_, values1_.ROOT_ID as ROOT1_3_, valueofman2_.id as VALUE2_3_, valueofman2_.id as id5_0_ from ROOT_OF_MANY_TO_MANY rootofmany0_ left outer join MAPPING_TABLE values1_ on rootofmany0_.id=values1_.ROOT_ID left outer join VALUE_OF_MANY_TO_MANY valueofman2_ on values1_.VALUE_ID=valueofman2_.id where rootofmany0_.id=?
> binding '1' to parameter: 1
> returning '1' as column: ROOT1_3_
> returning '32768' as column: VALUE2_3_
> done processing result set (1 rows)
> <returns previously persisted root>
> delete from MAPPING_TABLE where ROOT_ID=?
> binding '1' to parameter: 1
> delete from VALUE_OF_MANY_TO_MANY where id=?
> binding '32768' to parameter: 1
> delete from ROOT_OF_MANY_TO_MANY where id=?
> binding '1' to parameter: 1
> listing entities:
> ValueOfManyToMany{id=32768}
> <deletes all DB content including ValueOfManyToMany instance>
> delete from VALUE_OF_MANY_TO_MANY where id=?
> binding '32768' to parameter: 1
> <throws exception>
> [/code]
> [b]Debug level Hibernate log excerpt:[/b]
> Cropped to the tail of first delete:
> [code]
> DEBUG: Flushed: 0 insertions, 0 updates, 1 deletions to 1 objects
> DEBUG: Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
> DEBUG: listing entities:
> DEBUG: ValueOfManyToMany{id=32768}
> DEBUG: executing flush
> DEBUG: registering flush begin
> DEBUG: Deleting entity: [ValueOfManyToMany#32768]
> DEBUG: about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
> DEBUG: opening JDBC connection
> DEBUG: delete from ValueOfManyToMany where id=?
> DEBUG: preparing statement
> DEBUG: binding '32768' to parameter: 1
> DEBUG: Adding to batch
> DEBUG: Executing batch size: 1
> ERROR: Exception executing batch: 
> 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:93)
> 	at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
> 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
> 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
> 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
> 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
> 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:993)
> 	at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:388)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:363)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:774)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:770)
> 	at RootOfManyToManyTest.test_manyToManyRelationship(RootOfManyToManyTest.java:40)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> 	at java.lang.reflect.Method.invoke(Unknown Source)
> 	at junit.framework.TestCase.runTest(TestCase.java:154)
> 	at junit.framework.TestCase.runBare(TestCase.java:127)
> 	at junit.framework.TestResult$1.protect(TestResult.java:106)
> 	at junit.framework.TestResult.runProtected(TestResult.java:124)
> 	at junit.framework.TestResult.run(TestResult.java:109)
> 	at junit.framework.TestCase.run(TestCase.java:118)
> 	at junit.framework.TestSuite.runTest(TestSuite.java:208)
> 	at junit.framework.TestSuite.run(TestSuite.java:203)
> 	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
> 	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
> DEBUG: about to close PreparedStatement (open PreparedStatements: 1, globally: 1)
> DEBUG: closing statement
> ERROR: Could not synchronize database state with session
> 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:93)
> 	at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:79)
> 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:58)
> 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
> 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
> 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:297)
> 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
> 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:993)
> 	at org.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:388)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.execute(HibernateTemplate.java:363)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:774)
> 	at org.springframework.orm.hibernate3.HibernateTemplate.delete(HibernateTemplate.java:770)
> 	at RootOfManyToManyTest.test_manyToManyRelationship(RootOfManyToManyTest.java:40)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> 	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
> 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
> 	at java.lang.reflect.Method.invoke(Unknown Source)
> 	at junit.framework.TestCase.runTest(TestCase.java:154)
> 	at junit.framework.TestCase.runBare(TestCase.java:127)
> 	at junit.framework.TestResult$1.protect(TestResult.java:106)
> 	at junit.framework.TestResult.runProtected(TestResult.java:124)
> 	at junit.framework.TestResult.run(TestResult.java:109)
> 	at junit.framework.TestCase.run(TestCase.java:118)
> 	at junit.framework.TestSuite.runTest(TestSuite.java:208)
> 	at junit.framework.TestSuite.run(TestSuite.java:203)
> 	at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
> 	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
> 	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
> DEBUG: registering flush end
> DEBUG: closing session
> DEBUG: performing cleanup
> DEBUG: releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
> DEBUG: after transaction completion
> DEBUG: transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
> DEBUG: after transaction completion
> [/code]
> [b]Question:[/b] Is this a bug in Hibernate? My understanding is that Hibernate's state becomes different to that of the DB, though all deletions are explicitly mentioned in DEBUG logging.

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://opensource.atlassian.com/projects/hibernate/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        



More information about the hibernate-issues mailing list