View Full Version : A question about the Transaction attribute
dimitrod
03-16-2006, 03:27 PM
I have been playing around with the Hibernate intergation in Spring and looked at the samples in the sandbox folder and I have a few questions about the Transaction attribute. Correct me if I am wrong but according to what I have understood the operations defined in a dao method marked with this attribute should be executed in a transaction which means that if an exception occurs in the method the transaction must be rolled back.
I wrote the following method in my dao in order to test this:
[Transaction()]
public void PerformOperationsOnObject(TestObject to) {
HibernateTemplate.Save(to);
to.Name = "New name";
HibernateTemplate.SaveOrUpdate(to);
throw new Exception("Unexpected exception occured");
HibernateTemplate.Delete(to);
}
When I call this method the object is persisted in the database, it is updated afterwards and the exception is thrown but no rollback is performed - the object remains in the database. Is this the normal behaviour?
ITestObjectDao dao = (ITestObjectDao)ctx["testObjectDaoTransProxy"];
TestObject toGeorge = new TestObject();
toGeorge.Name = "George";
toGeorge.Age = 33;
dao.PerformOperationsOnObject(toGeorge);
The configuration I used is the one from the templateTests.xml file
<object id="testObjectDaoTransProxy" type="Spring.Transaction.Interceptor.TransactionProxyFac toryObject, Spring.Data">
<property name="PlatformTransactionManager" ref="hibernateTransactionManager"/>
<property name="Target">
<object type="Spring.Data.Orm.NHibernate.NHTestObjectDao, Spring.Data.Orm.NHibernate.Integration.Tests">
<property name="SessionFactory" ref="SessionFactory"/>
</object>
</property>
<property name="ProxyInterfaces" value="Spring.Data.Orm.NHibernate.ITestObjectDao"/>
<property name="TransactionAttributes">
<name-values>
<add key="PerformOperationsOnObject*" value="PROPAGATION_REQUIRED"/>
<add key="Create*" value="PROPAGATION_REQUIRED"/>
<add key="Delete*" value="PROPAGATION_REQUIRED"/>
<add key="Update*" value="PROPAGATION_REQUIRED"/>
<add key="Find*" value="PROPAGATION_REQUIRED"/>
</name-values>
</property>
</object>
What would be the easiest way to implement transaction support for my dao methods? I would greatly appreciate your suggestions.
Thanks in advance.
Mark Pollack
03-17-2006, 08:45 PM
Hi,
The configuration you have looks fine. I was also able to reproduce the same (wrong) behavior. I will look into the implementation and make a fix shortly. Thanks!
Cheers,
Mark
Mark Pollack
03-17-2006, 09:51 PM
Hi,
I changed the implementation of DefaultTransactionAttribute.RollbackOn. This method determines the rollback policy based on exception type. The previous implementation would rollback only if a SystemException was thrown. I've changed it now to always return true, meaning any type of exception thrown will cause a rollback. You can make the change yourself or get the latest from cvs/daily build.
I'd be interested in your opinion on what type of exception rollback rules you would like to see exposed. I was thinking we can allow the specification of which exceptions will cause rollback by specifying them explicity when declaring the TransactionProxyFactoryObject. If no exceptions were explicity specified, then we would rollback on any exception type.
Cheers,
Mark
dimitrod
03-20-2006, 09:01 PM
Mark, thank you very much for this clarification. It has always been a pleasure for me to use the Spring Framework knowing that such great professionals stand behind it.
I was thinking we can allow the specification of which exceptions will cause rollback by specifying them explicity when declaring the TransactionProxyFactoryObject. If no exceptions were explicity specified, then we would rollback on any exception type
This sounds good to me but what if we have different exception policy rollback for the different daos and even for the different dao methods? Instead of declaring the exceptions for the TransactionProxyFactoryObject wouldn't it be better to declare them for each dao? I don't know wether this would be easy to implement.
Mark Pollack
03-20-2006, 09:26 PM
Hi,
I think the current approach to the handling of exceptions that Spring.Java can be applied here. This way specifics method name as key and the trio of propagation properties, isolation level, and exception type to commit on as shown below
<property name="TransactionAttributes">
<name-values>
<add key="PerformOperationsOnObject*" value="PROPAGATION_REQUIRED, +ExceptionTypeOneToCommitOn"/>
<add key="Create*" value="PROPAGATION_REQUIRED, +ExceptionTypeTwoToCommitOn"/>
</name-values>
</property>
If you needed to handle the case of the same named DAO method having different exception policy, I'd say to change the code to be more consistent although nothing prevents you from specifiying individual transaction attribute for each DAO. Common usage puts them in a base abstract configuration object and then each DAO object inherits form that. To take from a Java example, cause don't have a C# one easily available.
<bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.Transa ctionProxyFactoryBean"
abstract="true">
<property name="transactionManager" ref="transactionManager"/>
<property name="transactionAttributes">
<props>
<prop key="create*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="delete*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="update*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="do*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="add*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="remove*">PROPAGATION_REQUIRED,-BusinessServiceException</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly,-BusinessServiceException</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly,-BusinessServiceException</prop>
</props>
</property>
</bean>
And then each DAO would have something like
<bean id="EntitlementDao" parent="baseTransactionProxy">
<property name="target" ref="EntitlementDaoTarget"/>
</bean>
where EntitlementDaoTarget is your POJO.
Whaddya think.
Cheers,
Mark
dimitrod
03-21-2006, 08:14 PM
Hi,
The java approach seems fine to me. I've used a lot Spring.Java and it would be wonderful if I can find the same functionality in .NET!
Talking to Java this reminds me that in Spring.Java there is a filter which implements the session-per-request pattern to manage hibernate sessions in a web application (OpenSessionInViewFilter.java). Do you plan to develop an HttpModule which would do the same job in an ASP.NET application? It would be very useful for the lazy collections.
I have also another question concerning the criteria API. In Java we use anonymous classes to create a criteria:
public List getUsersByExample(final User user) {
return (List) getHibernateTemplate().execute(new HibernateCallback() {
public Object doInHibernate(Session session) throws HibernateException {
return session.createCriteria( User.class )
.add( Example.create(user) )
.list();
}});
}
What would be the correct way to use criterias in c#?
Mark Pollack
03-21-2006, 08:28 PM
Hi,
Yes, there was another thread (http://forum.springframework.net/viewtopic.php?t=289) on the topic of open session in view. The only difference in approach to what that article does is that we will store the session inside thread local storage so that it is accessible by HibernateTemplate, or SessionFactoryUitls if you would like to get the session directly.
Cheers,
Mark
nokiola
03-23-2006, 09:55 AM
A quote from nhibernate forum regarding storage of the session inside Thread Local Storage:
http://forum.hibernate.org/viewtopic.php?t=951984
baggins wrote:
The reason commonly cited for not using Thread Local Storage in ASP.NET is concerns regarding thread pooling. That is, the thread that services one request is not guaranteed to service the next. However, from what I can tell, this is only an issue if the intent is to share the same session instance between requests. If a new session is created for each request and guaranteed to be cleaned up at the end, then in my opinion there should be no issue in using Thread Local Storage.
Perhaps a better choice is to use the System.Runtime.Remoting.Messaging.CallContext class instead of TLS. This is the class that is used internally by ASP.NET to store the HTTP context object anyway. This can apparently be verified by using Reflector to view the HttpContext implementation.
ASP.NET 2.0 allows a single web request to switch threads, or for one thread to switch between requests before the first request is finished. If this is enabled you should never use TLS because your sessions will get mixed up.[/quote]
Mark Pollack
03-23-2006, 01:01 PM
Hi,
Thanks for the pointer, I guess we will have to do some investigation to see the most appropriate place to stash the session. From your quote though it sounds like with the usage I was thinking of, sharing the session just for the life of the request and not between requests, we should be fine.
Cheers,
Mark
nokiola
03-23-2006, 08:00 PM
Please notice that due to the way ASP.NET requests are serviced, it seems that for ASP.NET 2.0 is not recommended at all to store session in TLS.
May I point you in using HttpContext.Items for storing the session? I am using this in my apps and it seems to work perfectly. Also, maybe you can provide in Spring multiple ways to choose: TLS for Winforms, HttpContext for web (framework 2.0 and 1.1) and TLS (Session-per-request scenario) for framework version 1.1.
Mark Pollack
03-23-2006, 08:12 PM
Hi,
Maybe System.Runtime.Remoting.Messaging.CallContext would work for .NET 1.1 and 2.0 inside a web app and outside?
I'd ideally like HibernateTemplate/SessionFactoryUtils to store the hibernate session in just one location, no matter what the type of app it is or where the DAO layer is run. There is also a need to store the connection/transaction object from ADO.NET so that a hibernate operation could participate with-in an existing ADO.NET transaction. (i.e. AdoTemplate/ConnectionUtils)
If we have to manage two locations, we can, it is just not as clean a solution.
Thanks for the advice!
Cheers,
Mark
Erich Eichinger
03-23-2006, 09:05 PM
Maybe System.Runtime.Remoting.Messaging.CallContext would work for .NET 1.1 and 2.0 inside a web app and outside?
Hi,
I would also prefer CallContext since it is guaranteed to be always the same during a "logical thread" - no matter if any physical threadswitching occurs or not.
btw: in some rare cases involving async webrequests, even in NET 1.1 an asp.net request may start on one thread and finish on another.
oaky
Powered by vBulletin® Version 4.2.0 Copyright © 2013 vBulletin Solutions, Inc. All rights reserved.