Announcement Announcement Module
Collapse
No announcement yet.
Rollback a non-dao operation with AbstractTransactionalDataSourceSpringContextTests Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Rollback a non-dao operation with AbstractTransactionalDataSourceSpringContextTests

    Hi Folks,

    I'm working on a test class that extends AbstractTransactionalDataSourceSpringContextTests. When my tests exercise database operations through methods in my dao, all works well. But in one of my tests I want to prep the test by first inserting a test row into the db, running a test against it and have everything rollback automatically. The thing is, my inserting of the test row is not done through a dao method, I'm doing it through the hibernateTemplate object available in my test class. This is my onSetUpInTransaction():

    Code:
    protected void onSetUpInTransaction() throws Exception {
    		
    	insertedModel = new ModelContent();
    	insertedModel.setModelName("TestModel");
    	insertedModel.setModelType("Hatchback");
    	insertedModel.setModelYear("2007");
    	insertedModel.setModelDestPrice(43500.00f);
    	insertedModel.setModelDestPriceAK(44600.00f);
    		
    	hibernateTemplate.saveOrUpdate(insertedModel);
    		
    	System.out.println("*** Placed test model in db");
    		
    }
    I've overridden the onTearDownInTransaction() method, which the javadoc states the transaction is still open at this point:

    Code:
    protected void onTearDownInTransaction() throws Exception {
    	System.out.println("*** Attempt to rollback");
    	transactionManager.rollback(transactionStatus);
    }
    but when this method is invoked, I see this:

    Code:
    org.springframework.transaction.IllegalTransactionStateException: Transaction is already completed - do not call commit or rollback more than once per transaction
    	at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:556)
    	at org.springframework.test.AbstractTransactionalSpringContextTests.endTransaction(AbstractTransactionalSpringContextTests.java:236)
    	at org.springframework.test.AbstractTransactionalSpringContextTests.onTearDown(AbstractTransactionalSpringContextTests.java:184)
    	at org.springframework.test.AbstractDependencyInjectionSpringContextTests.tearDown(AbstractDependencyInjectionSpringContextTests.java:322)
    	at junit.framework.TestCase.runBare(TestCase.java:130)
    	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.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
    	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
    So, the question is, how do I rollback the insert that was not done through a dao method? I've tried performing the rollback at the end of the test method and I seem to get the same results. Any advice on this will help a lot. Thanks

  • #2
    You can only rollback a transaction once (as well as committing).

    If it is part of your regular test, it will be automaticaly be rolled back because it is executed in the same transaction (afaik).

    Else try using the onBeforeTransaction and onTearDownAfterTransaction. Inserting the record in the onBefore and deleting it on the onTearDown.

    Code:
    protected void onSetUpBeforeTransaction() throws Exception {		
    	insertedModel = new ModelContent();
    	insertedModel.setModelName("TestModel");
    	insertedModel.setModelType("Hatchback");
    	insertedModel.setModelYear("2007");
    	insertedModel.setModelDestPrice(43500.00f);
    	insertedModel.setModelDestPriceAK(44600.00f);
    		
    	hibernateTemplate.saveOrUpdate(insertedModel);
    		
    	System.out.println("*** Placed test model in db");
    		
    }
    Code:
    protected void onTearDownAfterTransaction() throws Exception {		
    	hibernateTemplate.delete(insertedModel);	
    	System.out.println("*** Removed test model from db");
    }
    Last edited by Marten Deinum; Jul 5th, 2006, 01:50 AM.

    Comment


    • #3
      Thanks for the reply...
      I did end up manually inserting and removing the test rows in my class's onSetUpInTransaction and onTearDownInTransaction methods. I also noticed that I needed to have this:

      Code:
      setDefaultRollback(false);
      in my constructor, basically turning off any sort of automatic rollback. So the take home message to all of this (and anybody feel free to correct me if I'm wrong) is that automatic transaction rollback only happens on database operations that are performed within the methods of your dao, all other operations must be manually rolled back in your tear down methods.

      Thanks again for your help.

      Comment


      • #4
        Originally posted by mikeottinger
        Thanks for the reply...
        I did end up manually inserting and removing the test rows in my class's onSetUpInTransaction and onTearDownInTransaction methods. I also noticed that I needed to have this:

        Code:
        setDefaultRollback(false);
        in my constructor, basically turning off any sort of automatic rollback. So the take home message to all of this (and anybody feel free to correct me if I'm wrong) is that automatic transaction rollback only happens on database operations that are performed within the methods of your dao, all other operations must be manually rolled back in your tear down methods.

        Thanks again for your help.
        I don't think it matters if DB changes are done through daos or not. The hibernatetemplate itself is transaction aware and daos do nothing more then simply use it, just like you would do it manually. The question is why are you doing the rollback manually when the base class is doing it for you anyway? (that's why you are getting the exception, you can't rollback the same transaction twice)

        Comment


        • #5
          That's actually the core of my issue, my insert into the db in the onSetUpInTransaction() method is not being automatically rolled back. I've found that I have to actually remove the objects from the db by hand in my onTearDownInTransaction() method, Spring is doing no rollbacks whatsover. My theory might be that the hibernateTemplate used in my test is not transaction aware. I receive it from my app context in a setter:

          Code:
          public void setSessionFactory(SessionFactory sessionFactory) {
          	this.hibernateTemplate = new HibernateTemplate(sessionFactory);
          }
          could this be the culprit? Thanks again

          Comment


          • #6
            No - the template uses Hibernate which uses a transaction manager - what is your application context? Sounds like you have a misconfiguration - you can turn on logging and see if the transactions are started/rolledback/committed and if you are indeed using transactions or not.
            Also search the forum - this topic has been discussed plenty of times (and there are a lot of code samples also).

            Comment


            • #7
              Thanks for the reply Costin,

              I've been searching through the forum and haven't been able to find anything helpful. I set my spring logs to debug and have the following, I've bolded the important parts:

              Code:
              17:17:51,536  INFO JdbcTransactionObjectSupport:60 - JDBC 3.0 Savepoint class is available
              17:17:51,546 DEBUG HibernateTransactionManager:254 - Using transaction object [org.springframework.orm.hibernate3.HibernateTransactionManager$HibernateTransactionObject@1329642]
              17:17:51,546 DEBUG HibernateTransactionManager:281 - Creating new transaction with name [null]
              17:17:51,636 DEBUG HibernateTransactionManager:449 - Opened new Session [org.hibernate.impl.SessionImpl@8ceeea] for Hibernate transaction
              17:17:51,646 DEBUG HibernateTransactionManager:462 - Preparing JDBC Connection of Hibernate Session [org.hibernate.impl.SessionImpl@8ceeea]
              17:17:51,646 DEBUG DriverManagerDataSource:289 - Creating new JDBC Connection to [jdbc:mysql://localhost/mitsu]
              17:17:51,716 DEBUG JDBCTransaction:46 - begin
              17:17:51,716 DEBUG JDBCTransaction:50 - current autocommit status: true
              17:17:51,716 DEBUG JDBCTransaction:52 - disabling autocommit
              17:17:51,726 DEBUG HibernateTransactionManager:534 - Exposing Hibernate transaction as JDBC transaction [com.mysql.jdbc.Connection@56860b]
              17:17:51,726 DEBUG TransactionSynchronizationManager:162 - Bound value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [[email protected]9] to thread [main]
              17:17:51,846 DEBUG TransactionSynchronizationManager:162 - Bound value [org.springframework.orm.hibernate3.SessionHolder@1033a6f] for key [org.hibernate.impl.SessionFactoryImpl@f18e8e] to thread [main]
              17:17:51,846 DEBUG TransactionSynchronizationManager:214 - Initializing transaction synchronization
              @@@ About to insert test model @@@
              17:17:51,876 DEBUG CollectionFactory:114 - Creating [java.util.LinkedHashMap]
              17:17:51,946 DEBUG CollectionFactory:114 - Creating [java.util.LinkedHashMap]
              17:17:51,966  INFO SQLErrorCodesFactory:121 - SQLErrorCodes loaded: [DB2, HSQL, MS-SQL, MySQL, Oracle, Informix, PostgreSQL, Sybase]
              17:17:51,966 DEBUG SQLErrorCodesFactory:172 - Looking up default SQLErrorCodes for DataSource [[email protected]9]
              17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [[email protected]9] bound to thread [main]
              17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [[email protected]9] bound to thread [main]
              17:17:51,976 DEBUG SQLErrorCodesFactory:227 - Database product name cached for DataSource [[email protected]9]: name is 'MySQL'
              17:17:51,976 DEBUG SQLErrorCodesFactory:250 - SQL error codes for 'MySQL' found
              17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1033a6f] for key [org.hibernate.impl.SessionFactoryImpl@f18e8e] bound to thread [main]
              17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.orm.hibernate3.SessionHolder@1033a6f] for key [org.hibernate.impl.SessionFactoryImpl@f18e8e] bound to thread [main]
              17:17:51,976 DEBUG HibernateTemplate:358 - Found thread-bound Session for HibernateTemplate
              17:17:52,076 DEBUG HibernateTemplate:382 - Not closing pre-bound Hibernate Session after HibernateTemplate
              @@@ Test model has been inserted @@@
              @@@ Test the model @@@
              17:17:52,076 DEBUG HibernateTransactionManager:673 - Triggering beforeCompletion synchronization
              17:17:52,076 DEBUG HibernateTransactionManager:581 - Initiating transaction rollback
              17:17:52,076 DEBUG HibernateTransactionManager:599 - Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@8ceeea]
              17:17:52,076 DEBUG JDBCTransaction:132 - rollback
              17:17:52,127 DEBUG JDBCTransaction:173 - re-enabling autocommit
              17:17:52,127 DEBUG JDBCTransaction:143 - rolled back JDBC Connection
              17:17:52,137 DEBUG HibernateTransactionManager:697 - Triggering afterCompletion synchronization
              17:17:52,137 DEBUG TransactionSynchronizationManager:265 - Clearing transaction synchronization
              17:17:52,137 DEBUG TransactionSynchronizationManager:185 - Removed value [org.springframework.orm.hibernate3.SessionHolder@1033a6f] for key [org.hibernate.impl.SessionFactoryImpl@f18e8e] from thread [main]
              17:17:52,177 DEBUG TransactionSynchronizationManager:185 - Removed value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [[email protected]9] from thread [main]
              17:17:52,187 DEBUG HibernateTransactionManager:659 - Closing Hibernate Session [org.hibernate.impl.SessionImpl@8ceeea] after transaction
              17:17:52,187 DEBUG SessionFactoryUtils:785 - Closing Hibernate Session
              yet, my inserted data doesn't disappear. Here's what my onSetUpInTransaction looks like:

              Code:
              protected void onSetUpInTransaction() throws Exception {
              		
              		System.out.println("@@@ About to insert test model @@@");
              		
              		insertedModel = new ModelContent();
              		insertedModel.setModelName("TestModel");
              		insertedModel.setModelType("Hatchback");
              		insertedModel.setModelYear("2007");
              		insertedModel.setModelDestPrice(43500.00f);
              		insertedModel.setModelDestPriceAK(44600.00f);
              		
              		hibernateTemplate.saveOrUpdate(insertedModel);
              		
              		System.out.println("@@@ Test model has been inserted @@@");
              		
              	}
              and finally here's my context file:

              Code:
              <beans>
              
              	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              		<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
              		<property name="url" value="jdbc:mysql://localhost/mitsu"/>
              		<property name="username" value="root"/>
              		<property name="password" value="***"/>
              	</bean>
              
              	<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
              		<property name="dataSource" ref="dataSource"/>
              		<property name="mappingResources">
              			<list>
              				<value>com/organic/mitsu/hib/ModelContent.hbm.xml</value>
              				<value>com/organic/mitsu/hib/Trim.hbm.xml</value>
              				<value>com/organic/mitsu/hib/Accessory.hbm.xml</value>			
              			</list>
              		</property>
              		<property name="hibernateProperties">
              			<props>
              				<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
              				<prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
              				<prop key="hibernate.cache.use_query_cache">true</prop>
              			</props>
              		</property>
              	</bean>
              	
              	<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
              		<property name="sessionFactory" ref="sessionFactory"/>
              		<property name="dataSource" ref="dataSource"/>
              	</bean>
              	
              	<bean id="modelDao" class="com.organic.mitsu.dao.ModelDaoImpl">
              		<property name="sessionFactory" ref="sessionFactory"/>
              	</bean>
              	
              	<bean id="modelServiceTarget" class="com.organic.mitsu.service.ModelServiceImpl" abstract="false">
              		<property name="modelDao" ref="modelDao"/>
              	</bean>
              	
              	<bean id="modelManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
              		<property name="transactionManager"><ref local="transactionManager"/></property>
              		<property name="target"><ref local="modelServiceTarget"/></property>
              		<property name="transactionAttributes">
              			<props>
              				<prop key="list*">PROPAGATION_REQUIRED,readOnly</prop>
              				<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
              				<prop key="updateModel">PROPAGATION_REQUIRED</prop>
              			</props>
              		</property>
              	</bean>
              	
              </beans>
              I'm out of ideas on this, if anything looks suspicious I'd love to see it. Thanks again

              Comment


              • #8
                Little update on this. After some forum searching, I switched my MySql table to use the InnoDB, this resolved the issues. BUT, I work with a SQL Server instance on my app at work and I could have sworn I had the same issues. So I'll take a look at this first thing tomorrow. Does anybody know if SQL Server has the same type of transaction requirements as MySQL, or does SQL Server default to enabling transactions? Thanks

                Comment


                • #9
                  Originally posted by mikeottinger
                  Little update on this. After some forum searching, I switched my MySql table to use the InnoDB, this resolved the issues. BUT, I work with a SQL Server instance on my app at work and I could have sworn I had the same issues. So I'll take a look at this first thing tomorrow. Does anybody know if SQL Server has the same type of transaction requirements as MySQL, or does SQL Server default to enabling transactions? Thanks
                  Afaik, the only "database" that's not transactional and still widely used these days is old MySql.

                  Comment


                  • #10
                    Hi Folks,

                    Well, I'm not sure what I had done, but this issue is officially resolved I might have had something sublty off in one of my configurations or test classes. I hadn't taken a look at this issue in a few days, I think giving it a break and returning to it with a fresh perspective helped. Once again, thank you guys for all your help.

                    - Mike

                    Comment

                    Working...
                    X