I was a part of a team that have developed
several applications using
Struts,
Spring and
Hibernate
together, and one of the problems that have
faced us while using
Hibernate
was the rendering of the view. The problem
is that when you retrieve an object 'a'
of persistence class 'A' that has an instance
'b' of persistence class 'B', and this relation
is lazily loaded, the value of 'b' will
be 'null'. This will cause a "LazyInitializationException"
while rendering the view (if you need the
value of 'b' in the view of course).
A quick and easy solution to that is to
set the "lazy" attribute
to "false" so that 'b'
would be initialized while fetching 'a',
but this is not always a good idea. In case
of many-to-many relationships,
using non-lazy relations might result in
loading the entire database into the memory
using a great number of "select"
statements, which will result to very poor
performance, and to massive memory consumption.
Another solution is to open another unit
of work in the view, which is really bad
for several reasons. First of all, as a
design concept, the layers of your application
should be loosely coupled, and by doing
the previous practice you have coupled the
presentation layer with you DB layer, which
is bad. Another thing is that this destroys
the separation of concerns concept.
The solution to this problem can be done
by keeping the hibernate session alive until
the view is rendered, and this is what
Hibernate
introduced as the
Open Session In View Design Pattern.
Since the
Hibernate
session will be opened, trying to retrieve
'b' in the view will cause
Hibernate
to go and fetch it from the DB. In a web
application, this can be done through a
filter/interceptor.
Spring framework comes with both a
filter and an
interceptor, so that you don't have
to write your own. The problem that might
face you, if you're using
spring's HibernateTemplate,without
doing your own session and transaction management,
is that you will not be able to save, edit
or delete anything, since both the filter
and the interceptor provided by
spring set the flush mode of the session
to "NONE".
A solution to that, which I've learned from
a friend of mine recently is to extend the
filter provided by spring, override the
getSession method to
set a different flush mode, and override
the closeSession method
to flush the session before closing it.
The sample code is shown below:
public class HibernateFilter extends OpenSessionInViewFilter {
@Override
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
//set the FlushMode to auto in order to save objects.
session.setFlushMode(FlushMode.AUTO);
return session;
}
@Override
protected void closeSession(Session session, SessionFactory sessionFactory) {
try{
if (session != null && session.isOpen() && session.isConnected()) {
try {
session.flush();
} catch (HibernateException e) {
throw new CleanupFailureDataAccessException("Failed to flush session before close: " + e.getMessage(), e);
} catch(Exception e){}
}
} finally{
super.closeSession(session, sessionFactory);
}
}
}
By using this filter, you will be able to render the view easily, without having to set the "lazy" attribute to "false", or to open a hibernate session in the view, but you have to take care not to change any values of the persistence object in the view, because those changes will be saved to the DB at the end of the request. This is the main reason why the flush mode is set to "NONE" in the original filter and interceptor.
By Alaa Nassef
5 comments:
Hi,
Just wondering have you encountered problems with this approach where users use multiple quick clicks on links.
I get a "Illegal attempt to associate a collection with two open sessions" error.
I believe that this is where one session has not been closed yet by the filter and another is opened.
Any ideas?
Is there a way to use this solution with Spring-ws?
Thanks a lot, I felt identified with your situation in several ways.
Jose Luis.
You say: to open another unit of work in the view is wrong in the sense of loosely coupled layers (by coupling the presentation layer with you DB layer, which is bad.)
But we are not changing the coupling there. We are just opening a new persistence context (unit of work) to render the view. This is not relevant to the concept of "seperation of concerns" or loosely coupled layers.
The only problem of that approach is, you are creating a new unit of work for rendering which should normally be part of the previous unit of work.
Again, I see nothing related to dependencies between layers. And "seperation of concerns" is not also relevant I think.
The later versions of Spring seem to have fixed this problem, also note that Spring allows you to explicitly set the flush mode:
setFlushMode
public void setFlushMode(int flushMode)Set the flush behavior to one of the constants in this class. Default is FLUSH_AUTO.
See Also:
setFlushModeName(java.lang.String), FLUSH_AUTO
From the latest Spring documentation.
-Ak
Post a Comment