Results 1 to 10 of 10

Thread: Rollback a non-dao operation with AbstractTransactionalDataSourceSpringContextTests

  1. #1

    Default 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. #2
    Join Date
    Jun 2006
    Location
    The Netherlands
    Posts
    13,625

    Default

    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 at 01:50 AM.

  3. #3

    Default

    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.

  4. #4
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    Quote 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)

  5. #5

    Default

    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

  6. #6
    Join Date
    Jan 2005
    Location
    Bucharest, Romania
    Posts
    5,403

    Default

    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).
    Costin Leau
    SpringSource - http://www.SpringSource.com- Spring Training, Consulting, and Support - "From the Source"
    http://twitter.com/costinl
    Please use [ c o d e ] [ / c o d e ] tags

  7. #7

    Default

    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 [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19] 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 [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19]
    17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19] bound to thread [main]
    17:17:51,976 DEBUG TransactionSynchronizationManager:137 - Retrieved value [org.springframework.jdbc.datasource.ConnectionHolder@b122a1] for key [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19] bound to thread [main]
    17:17:51,976 DEBUG SQLErrorCodesFactory:227 - Database product name cached for DataSource [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19]: 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 [org.springframework.jdbc.datasource.DriverManagerDataSource@7a5a19] 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

  8. #8

    Default

    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

  9. #9
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    Quote 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.

  10. #10

    Default

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •