Community   SpringSource   Projects    Downloads    Documentation    Forums    Training   Exchange   Blogs

Go Back   Spring Community Forums > Core Spring Projects > Core Container

Reply
 
Thread Tools Display Modes
  #1  
Old Jul 4th, 2006, 11:23 PM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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
Reply With Quote
  #2  
Old Jul 5th, 2006, 02:47 AM
Marten Deinum Marten Deinum is offline
Senior Member
 
Join Date: Jun 2006
Location: The Netherlands
Posts: 8,095
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 02:50 AM.
Reply With Quote
  #3  
Old Jul 5th, 2006, 12:12 PM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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.
Reply With Quote
  #4  
Old Jul 6th, 2006, 04:58 AM
dejanp dejanp is offline
Senior Member
 
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)
Reply With Quote
  #5  
Old Jul 7th, 2006, 01:58 PM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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
Reply With Quote
  #6  
Old Jul 9th, 2006, 02:48 PM
Costin Leau's Avatar
Costin Leau Costin Leau is offline
Spring DM Lead
Spring Modules TeamSpring Team
 
Join Date: Jan 2005
Location: Bucharest, Romania
Posts: 5,015
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
Reply With Quote
  #7  
Old Jul 9th, 2006, 08:27 PM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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
Reply With Quote
  #8  
Old Jul 9th, 2006, 10:19 PM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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
Reply With Quote
  #9  
Old Jul 10th, 2006, 05:20 AM
dejanp dejanp is offline
Senior Member
 
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.
Reply With Quote
  #10  
Old Jul 10th, 2006, 11:54 AM
mikeottinger mikeottinger is offline
Member
 
Join Date: Jun 2006
Posts: 74
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
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -5. The time now is 10:08 AM.


Contegix provides first-class managed hosting and partial sponsorship of these forums.

Powered by vBulletin® Version 3.8.4
Copyright ©2000 - 2010, Jelsoft Enterprises Ltd.