Your service objects should receive DAOs and other collaborating objects via bean references, instead of looking them up themselves. Have a look at the skeletons and sample apps that come with the Spring distribution for example configurations. Basically, it would look as follows:
Code:
<beans>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
...
</bean>
<bean id="myDataAccessObject" class="dao.MyDataAccessObject">
<property name="sessionFactory">
<ref bean="mySessionFactory"/>
</property>
</bean>
<bean id="myServiceObject" class="service.MyServiceObject">
<property name="dataAccessObject">
<ref bean="myDataAccessObject"/>
</property>
</bean>
</beans>
MyServiceObject just needs to expose a bean property "dataAccessObject" of type MyDataAccessObject, i.e. a setter method "setDataAccessObject(MyDataAccessObject)", to receive its collaborating DAO instance. It does not need to know about the Spring application context or any custom service locator - IoC applied.
Of course, you can proxy any of these beans: You just need to link the proxied beans as bean references in the application context definition then - your application objects do not have to care. From the outside, you will possibly just access "myServiceObject", as the rest are just collaborating objects that the service object internally needs to handle its responsibilities.
You could use Spring's ApplicationContext itself as service locator, instead of wrapping it with a custom service locator. You can simply fetch a reference to the ApplicationContext from within a Struts action via WebApplicationContextUtils.getWebApplicationContext(ServletContext), for example. With Spring's own web MVC, you don't need to do that in the first place, as you can use bean references again to link your web controllers with your service objects.
Regarding Session handling: You're right - NEVER pass Session instances around as method arguments. With or without HibernateInterceptor, your DAO will participate in transactions, as SessionFactoryUtils.getSession is transaction-aware. If no transaction, the DAO will simply execute non-transactionally with its own Session. Transaction detection works via ThreadLocals internally, but you don't have to worry about that.
Note that HibernateInterceptor is mainly there to avoid the need for SessionFactoryUtils.closeSessionIfNecessary calls in DAO implementations. Additionally, it allows to configure the Session outside of your DAO implementation via bean properties of the interceptor, e.g. the flushing behavior. When using HibernateInterceptor, you should fetch your session with allowCreate=false in any case - the interceptor handles the Session lifecycle then.
Alternatively, if you allow your getSession calls to create a Session and write proper try-catch blocks in your DAOs, you can get correct lifecycle behavior without HibernateInterceptor. The following would be proper DAO code, participating in transactions or executing non-transactionally if no current transaction:
Code:
public myDataAccessMethod() {
Session session = SessionFactoryUtils.getSession(getSessionFactory(), true);
try {
// do some Hibernate access with the Session
}
catch (HibernateException ex) {
SessionFactoryUtils.closeSessionIfNecessary(session, getSessionFactory());
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
Regarding the templating approach: If you need to throw checked application exceptions, it's indeed better to choose alternative ways, particularly for transaction demarcation: ignore TransactionTemplate then, TransactionInterceptor or the combo convenience class TransactionProxyFactoryBean are more appropriate choices. I tend to prefer such declarative transaction management in general, as it avoids repetitive TransactionTemplate code in each transactional service method.
HibernateTemplate is a bit different, as it not only provides the callback implementation mechanism but also a lot of pre-built convenience methods. For typical simple Hibernate access code, it can reduce your DAO implementation methods to one-liners:
Code:
public Account loadAccount(int id) {
return (Account) getHibernateTemplate().load(new Integer(id));
}
I recommend using those convenience methods for most cases, just falling back to manual Session handling if you really need to throw a checked exception from *within* Hibernate access code. It often turns out that checked application exceptions can easily be thrown *after* the Hibernate access code, which works nicely with HibernateTemplate too.
Juergen