This article describes some complementation for Open Session In View pattern for Java/Spring. But first I want to show just my point of view about the pattern itself. Many people consider this as an anti-pattern and tell that it never should be used. I think exactly opposite, and here are some explanations.
I code java (JEE) almost 10 years and in general after this experience I can tell that there are two architectures for Java webapps which I worked in (and many variations of course): service oriented architecture (or SOA/service design) and domain driven design (DDD).
Service oriented architecure is a perflectly layered application, with each layer with its own, and only one responsibility: we mostly have views, controllers, services and daos. A lot of people encourage this design, as the only valid one. It indeed looks very good on the paper and in diagrams - this is the cleanest one. But here are my experiences in working with such applications.
I’m talking about standard design with such design guidelines (the most common):
Second thing is that I’m talking about standard small-medium size webapps, they can be web portals or business application, they can be clustered using eg. standard tomcat clustering with load balancer. But I’m not talking about very big systems eg. for banking with 30 developers team and 2 mln of code lines, working on the server farm and integrating inside 15 standalone systems for various purposes. For such requirements I think the SOA is the right choice.
Now, what is wrong with this design for me? These projects, where we used such design were unnecessarily HUGE. The development team had to be much bigger than in DDD, the simple modifications to the application took a lot of hours and people spent 80% of time for doing mokey jobs, like moving data from one type of objects to another or struggling with LazyInitializationException. Sincerely, if I had chosen this design few years ago when I created my own application, I would have been out of the market already. I already know people who give up Java, and switched to different techs (like Python or Ruby) because “Java became the technology where you cannot do anything efficiently”. I believe this is not true, this is only the design problem.
Where does all this bloat come from? The main problem here is exactly the hibernate session management design. I consider that almost 90% of this bloat code come from this way of handling with Hibernate. I think all this design was created when there was no sensible ORM yet, but when it appeared, people just don’t understand how it could help to create applications really rapidly, using all benefits of ORM. And to create them with 20% of original team, and to have the product that really quickly can answer for the constantly changing requirements from the market.
So, what are my problems with SOA? Here are they:
BlogEntry
entity with collections of Comment
. What is problem with this? If you have eg. service method:BlogEntryDTO getBlogEntry(long id) {..}
BlogEntry
entity usage for the client code which doesn’t need them? Hmm… no, and you finally end up with:BlogEntryDTO getBlogEntry(long id) {..}
BlogEntryDTO getBlogEntryWithComments(long id) {..}
getBlogEntry()
methods in a service, plus fifty other services. Finally to do a simple thing you need to dig through hundreds of code lines, to find what you exactly need (or write another getBlogEntry()
method if you can’t).List<BlogEntryDTO> getUserBlogEntries(long userid) {..}
count(*)
calculation and limits for pagination, and the sorting capability as well. How many method do we need to write more to achieve this? It’s crazy.And how does it look in the DDD design?
For the people who advise anyway to use SOA, I can tell that I know that DDD is not a pure layered solution. But greater benefits for me is to have an application that have 30% of original (SOA) lines of code (and all benefits from this - time and cost of development, time and cost of modification, testing etc), than have a big bloat, but “pure layered” medal on my chest.
Of course, every pot has two handles. Now few words about when the SOA might be better than DDD in my opinion. It’s when you have a big part of application, where you don’t handle with web requests (like standard user request, or webservice request), but the work is done in internal threads (eg. schedulers, ESB listeners etc). DDD is highly bound to open-session-in-view pattern, usualy done by OpenSessionInViewFilter
(eg. from Spring) and the holding session opened just don’t work for internal threads. So in DDD you can imagine that you may have inconsistent design, because you need to handle with service layer in different way from request-bound threads and from internal threads. While in SOA, if you already developed all this stuff required to implement this pattern, everything is handled in the same way from both kinds of threads. Well, you’re right, but…
I have at least two solutions for this problem. The internal mechanisms usually use just some subset of your total business logic (like receiving tasks from some server and putting them to the db periodically). You can easily create small and thin layer of services, dedicated to work with internal threads, and expose the subset of logic that they need, in these services. Your internal services can be designed in standard SOA pattern, ie. to have session per transaction and AOP-ly defined transaction boundaries on methods. Your scheduler or whatever else can always start the work from some service method invocation, that does its job.
Another solution is my OpenSessionInThreadExecution
pattern, which is just the remake of OpenSessionOnViewFilter
from Spring. It assumes, that you always have some entry point in some background tasks, where you start doing you background work (like Runnable.run()
) - and this is perflectly true because somewhere you join to the internal thread with your logic. Using this bean you can simply wrap your execution with Runnable
, and pass to execute()
method, to be executed with opened session throughout whole execution. Here is the code:
import org.apache.log4j.Logger;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* This is a util class to keep session opened during whole single thread
* execution (like scheduler or mule service thread), works in the same manner
* as {@link OpenSessionInViewFilter}.
*
* @author Lukasz Frankowski (http://lifeinide.com)
*/
public class OpenSessionInThreadExecution implements InitializingBean {
protected static final Logger logger =
Logger.getLogger(OpenSessionInThreadExecution.class);
protected static OpenSessionInThreadExecution instance = null;
protected boolean singleSession = true;
protected FlushMode flushMode = FlushMode.MANUAL;
protected SessionFactory sessionFactory;
@Override
public void afterPropertiesSet() throws Exception {
if (instance == null)
instance = this;
else
throw new IllegalStateException(
"OpenSessionInThreadExecution should be defined as " +
"the only one singleton bean.");
}
public SessionFactory getSessionFactory() {
return sessionFactory;
}
@Required
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Set whether to use a single session for each request. Default is "true".
* <p>If set to "false", each data access operation or transaction will use
* its own session (like without Open Session in View). Each of those
* sessions will be registered for deferred close, though, actually
* processed at request completion.
*
* @see SessionFactoryUtils#initDeferredClose
* @see SessionFactoryUtils#processDeferredClose
*/
public void setSingleSession(boolean singleSession) {
this.singleSession = singleSession;
}
/**
* Return whether to use a single session for each request.
*/
protected boolean isSingleSession() {
return this.singleSession;
}
/**
* Specify the Hibernate FlushMode to apply to this filter's
* {@link org.hibernate.Session}. Only applied in single session mode.
* <p>Can be populated with the corresponding constant name in XML bean
* definitions: e.g. "AUTO".
* <p>The default is "MANUAL". Specify "AUTO" if you intend to use
* this filter without service layer transactions.
*
* @see org.hibernate.Session#setFlushMode
* @see org.hibernate.FlushMode#MANUAL
* @see org.hibernate.FlushMode#AUTO
*/
public void setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
}
/**
* Return the Hibernate FlushMode that this filter applies to its
* {@link org.hibernate.Session} (in single session mode).
*/
protected FlushMode getFlushMode() {
return this.flushMode;
}
public void execute(Runnable command) {
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
} else {
logger.debug("Opening single Hibernate Session in " +
"OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(
sessionFactory, new SessionHolder(session));
}
} else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
} else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
command.run();
} finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.
unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in " +
"OpenSessionInViewFilter");
closeSession(sessionHolder.getSession(), sessionFactory);
} else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
}
/**
* Get a Session for the SessionFactory that this filter uses.
* Note that this just applies in single session mode!
* <p>The default implementation delegates to the
* <code>SessionFactoryUtils.getSession</code> method and
* sets the <code>Session</code>'s flush mode to "MANUAL".
* <p>Can be overridden in subclasses for creating a Session with a
* custom entity interceptor or JDBC exception translator.
*
* @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use
* @throws DataAccessResourceFailureException
* if the Session could not be created
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#
* getSession(SessionFactory, boolean)
* @see org.hibernate.FlushMode#MANUAL
*/
protected Session getSession(SessionFactory sessionFactory)
throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = getFlushMode();
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
/**
* Close the given Session.
* Note that this just applies in single session mode!
* <p>Can be overridden in subclasses, e.g. for flushing the Session before
* closing it. See class-level javadoc for a discussion of flush handling.
* Note that you should also override getSession accordingly, to set
* the flush mode to something else than NEVER.
*
* @param session the Session used for filtering
* @param sessionFactory the SessionFactory that this filter uses
*/
protected void closeSession(Session session, SessionFactory sessionFactory) {
SessionFactoryUtils.closeSession(session);
}
public static OpenSessionInThreadExecution getInstance() {
return instance;
}
}
this is a copy from my old blogspot blog