PDA

View Full Version : Transaction problem in NUnit tests.



mmwaikar
03-05-2008, 03:47 PM
Hi,

I am trying to use the Transaction Management features in one of my applications that uses NHibernate as the data access technology.

My DAO class has the following methods -

[Transaction(TransactionPropagation.Supports)]
public T FindById(int id) {
return FindById(id, false);
}

[Transaction(TransactionPropagation.Supports)]
public T FindById(int id, bool shouldLock) {
T entity;

if (shouldLock) {
entity = (T)HibernateTemplate.Load(persitentType, id, LockMode.Upgrade);
}
else {
entity = (T)HibernateTemplate.Load(persitentType, id);
}

return entity;
}

[Transaction(TransactionPropagation.Required)]
public T Save(T entity) {
HibernateTemplate.Save(entity);
return entity;
}

[Transaction(TransactionPropagation.Required)]
public T SaveOrUpdate(T entity) {
HibernateTemplate.SaveOrUpdate(entity);
return entity;
}

[Transaction(TransactionPropagation.Required)]
public void Delete(T entity) {
HibernateTemplate.Delete(entity);
}

and my test for update looks like this -

[Test]
public void CanUpdate() {
Application a = _dao.FindById(1);
Assert.IsNotNull(a, "application cannot be null");
a.Name = "new application";
a.Description = "description";
a.Profile = "profile";
_dao.SaveOrUpdate(a);

Application a1 = _dao.FindById(a.Id);
Assert.IsNotNull(a1, "fetched obj cannot be null");
StringAssert.AreEqualIgnoringCase("new application", a1.Name);
StringAssert.AreEqualIgnoringCase("description", a1.Description);
StringAssert.AreEqualIgnoringCase("profile", a1.Profile);
}

Now when I run this test, with the Transaction mode of
[Transaction(TransactionPropagation.Supports)] on my FindById methods, I get the following error -

SpringTran.Dao.Tests.ApplicationDaoTest.CanUpdate:
Spring.Data.NHibernate.HibernateSystemException : Illegally attempted to associate a proxy with two open Sessions
----> NHibernate.LazyInitializationException : Illegally attempted to associate a proxy with two open Sessions

and when I change the Transaction mode on my FindById methods to
[Transaction(ReadOnly=true)] I get this error -

SpringTran.Dao.Tests.ApplicationDaoTest.CanUpdate:
NHibernate.LazyInitializationException : Could not initialize proxy - the owning Session was closed.

Now I have the following questions -
1) What's the difference between the two transaction modes - ReadOnly=true and TransactionPropagation.Supports?
2) Which one of the two should be used for FindById or FindAll types of methods and why?
3) How does Spring manage NHibernate sessions in Unit testing scenarios?
4) If OSIV is not used, does Spring default to managing sessions in TLS? If yes, do we need to configure it, or it is automatic?

I turned the logging on and see two sessions being opened.
So is this a bug or I am doing something wrong here?

Please let me know.

Thanks and regards,
Manoj.

Erich Eichinger
03-06-2008, 10:08 AM
Hi,

no, Spring doesn't automatically use OSIV functionality. You can use SessionScope() to wrap your dao calls into a single session:



using( new SessionScope())
{
... your dao calls here
}


Another solution is to use Spring.Testing.NUnit's "AbstractTransactionalSpringContextTests" class as a base class for your testfixture. It addresses exactly this problem.

hope this helps,
Erich

steinard
03-06-2008, 10:32 AM
Hi!

I would not use the transactional attribute on the dao layer because you get nested sessions (and it becomes harder to control the short lived sessions). This means that you potentially indirectly (due to object graph relationships) load an entity that is already loaded by another open session.

You get lazy exceptions when you leave one method and is returned back to another transactional method, because the inner session is closed. To make this work you must reattach the just loaded entity to the outer session. I see no point in that.


Now I have the following questions -
1) What's the difference between the two transaction modes - ReadOnly=true and TransactionPropagation.Supports?
2) Which one of the two should be used for FindById or FindAll types of methods and why?1. Transaction ReadOnly sets the FlushMode of the session to FlushMode.Never and has nothing to do with how an transaction is propagated into a new context.
TransactionPropagation.Supports means that the method supports to run in an existing transaction if a thread bound transaction exists otherwise a new session and a transaction will be created.

2. Whenever you are searching the database for entities you do not really need a transactional scope. A session scope is good enough. By using the default scope of a session (this means the session's FlushMode is set to FlushMode.Never) equals the use of Transaction(ReadOnly=true) attribute.

For your methods above, the use of the Transactional attribute only make matters more complex then necessary, I recommend putting these attributes on your service implementation. This means that you easily can control the life of the session/transaction in your unit test.

Cheers,
Steinar.

mmwaikar
03-06-2008, 02:58 PM
Thanks a lot for your detailed replies.

However, my doubt is that if my DAO layer doesn't use transactions, then do I have to use services for each and every one of my DAO? i.e.

1) To support n DAOs, should there be n Services?
2) If the above is true, then should the simple method calls like FindById or FindAll, also be routed through the Service layer?
3) Or, is it that my Service layer should only wrap those DAO methods that actually need transaction support i.e. Insert, Update and Delete?

Actually, I just noticed that the Spring.Northwind example application uses -

In DAO -
a) No attribute for FindById method
b) [Transaction(ReadOnly = false)] attribute for Save, SaveOrUpdate and Delete methods

In FulfillmentService -
a) [Transaction(ReadOnly=false)] for ProcessCustomer method

Whereas the Spring.TxQuickStart example application uses -

In AccountManager -
a) [Transaction(TransactionPropagation.Required)] for DoTransfer method

4) So which one of the two [Transaction(TransactionPropagation.Required)] or [Transaction(ReadOnly = false)] is more appropriate for methods on the Service layer?

I am really confused so your replies are greatly appreciated.

steinard
03-06-2008, 05:43 PM
Hi!



1) To support n DAOs, should there be n Services?
2) If the above is true, then should the simple method calls like FindById or FindAll, also be routed through the Service layer?
3) Or, is it that my Service layer should only wrap those DAO methods that actually need transaction support i.e. Insert, Update and Delete?
1) It depends how you would like to solve this problem. If you only need basic methods like CRUD and generic finds then these can be defined in a generic Service<T> class instantiated in the IoC container. In my experience, I do not get that many services, I have an abstract service implementation that does all the generic stuff and 6 specific services (like ContentManagementService, UserGroupService, EfmDomainService etc, grouping the delegation of work to the various domain objects that naturally belong in that domain). If you have n DAOs for n domain classes, then of course this is no way to go. I do not use the 1 to 1 relationship between domain classes and daos. In my case, most services have access to only one dao while some services have no dao access at all.

The good thing about having a service is that you can reuse the same session/transaction across all dao calls and across different daos. This means that you will not get session conflict expcetions and lazy initialization exceptions while processing that service method, while also benefiting from the 1st level cache. It's okay to put transaction attributes on the dao layer if you do not call other dao methods which also have transaction attributes. Sooner or later you will get trouble, and even if you manage to steer clear of session problems, then maybe team members might end up creating nested sessions. You are creating a web-app right? If so I think you naturally need to cross some network boundary so why not open the session on middleware entry and close it on middleware response (OSIV)?

2) Yes, this is so generic that you would only do this for one service impl and reuse that service eiter through generics or inheritance.

3) I think I would say yes here, I would even have a service even if I made a single user winform with a database on the local machine. The clue is that with Spring.Net service exporters, it's just a matter of configuration and you have turned your local app (if structured accordingly) into a three tiered application. (Flexibility).


4) So which one of the two [Transaction(TransactionPropagation.Required)] or [Transaction(ReadOnly = false)] is more appropriate for methods on the Service layer?I think these are default settings so if you write:
[Transaction]
then you should get exactly the same behavior as above. TransactionPropagation controls how a transaction scope should flow into a new transactional method while ReadOnly manipulates the sessions FlushMode. Whenever you just fetch data from the DB (reading), then setting the ReadOnly=true ensures that the transaction will not write anything to the DB when the session closes by accident.


If you decide to use the transactional attribute on your DAOs then make sure to always decorate your methods with:
[Transaction(TransactionPropagation.Required)]
and not:
[Transaction(TransactionPropagation.Supported)]

If I've understood this correctly, then:
Required will:
- create a new transaction if no transaction exists
- participate in an existing transaction if a transaction exists
- propagate (pass on) transaction context if possible
- close transaction if created on method entry

Supported will:
- create a new transaction if no transaction exists
- participate in an existing transaction if a transaction exists
- never propagate self-created transaction context
- close transaction if created on method entry

I think this is the problem you get with your FindById methods, the outer scope does not pass on the defined transaction to the inner transactional method on method invocation. The inner scope creates a new transaction which is closed on method end and the loaded entity returned to the outer transactional method references a closed session.

I have a link somewhere about TransactionPropagations...I'll try to dig it up.
Hope this helps,
Steinar.

mmwaikar
03-09-2008, 03:46 AM
Hi,

Thanks for the detailed reply :)

In my case, the application is going to be a Windows service, so I can't use the OSIV.

steinard
03-09-2008, 06:43 AM
Hi!



In my case, the application is going to be a Windows service, so I can't use the OSIV.
You should still be able to use the SessionScope implementation if you want to mimic OSIV behavior. That is what I do on my middleware which by the way also is running as a windows service.

You can read more about transaction propagations here (http://www.onjava.com/pub/a/onjava/2001/06/06/j2ee_trans.html?page=1) ;)


Cheers,
Steinar.

Erich Eichinger
03-09-2008, 10:04 AM
Hi,

please checkout the reference docs on using SessionScope (http://www.springframework.net/doc-latest/reference/html/orm.html#d0e13491):



using (new SessionScope())
{
... do multiple operations with a single session, possibly in multiple transactions.
}


Basically OSIV just spans a SessionScope across a request. But you can use SessionScope to span a single session across anything that fits of your "units of work".

-Erich

mmwaikar
03-17-2008, 03:41 AM
Thanks for the help and replies. I am using a Service to wrap my DAO calls in a SessionScope, and it works fine.

steinard
03-17-2008, 06:16 AM
Hi!

Great to hear that things are working.

Cheers,
Steinar.