Spring: SessionFactory Prototype
May 18, 2006
In the spirit of Rod Johnson's naming conventions, I was not ashamed when I decided to name this class SessionFactoryPrototypeFactoryBean. Before I get into details, let me present the problem. Spring provides a factory bean out of the box to establish Hibernate SessionFactory instances, or, should I say instance. In fact, this class isn't much of a factory at all. At its very best, it can create and handoff exactly one instance of a Hibernate SessionFactory. It acts as a transparent proxy to the SessionFactory, creating a new instance in the afterPropertiesSet method and passing all subsequent method calls directly to the underlying SessionFactory. I offer a solution that will allow prototyping of the SessionFactory that this bean manages (saving you all some grief along the way).
So why the pain? Well, a business requirement arose in which we needed to dynamically switch between different instances of a SessionFactory backing the same database schema. There are two prime use cases calling for this functionality. In one case, it might be necessary to connect to a different regional data source depending on credentials or regionality of the user, or perhaps the same user needs to connect to databases of different regions in order to manage or report on them. The second case would be when a single application needs to access different environments in order to migrate data between them.
Let's look at what is wrong with LocalSessionFactoryBean. First off, it hardcodes the return for the isSingleton method to true. This makes configuration as a prototype impossible. Naturally, a factory is a singleton, but in the case of a spring factory bean, the flag indicates how the underlying bean should be managed. So, no matter how the factory is configured, it will always return the same SessionFactory. The second problem is that the SessionFactory is created in the afterPropertiesSet method. As everyone knows, SessionFactory instances take a long time to create and if the connection to the DataSource is slow, it makes matters worse (resulting in an unnecessary slow startup). Finally, because a Hibernate SessionFactory maps 1:1 with a DataSource, it is necessary to be able to notify the factory that the DataSource has changed. You might think that this is straightforward since there is a setDataSource on the LocalSessionFactoryBean. Not so. Since the LocalSessionFactoryBean passes all calls directly to SessionFactory, it is not possible to reconfigure it after initial load.
The solution comes in three parts. First, the return for the isSingleton method is going to be reversed to true (I suppose it could be made configurable, but I will leave that open for discussion and refactoring). The second change is to add a preinitialize flag, which will indicate whether the SessionFactory should be created eagerly, or whether it should be deferred until first use. Finally, the most significant change is to allow the DataSource to be looked up dynamically by providing a spring bean name which might have a factory-method or is otherwise made mutable (within a synchronized block of course).
There is also a thread on the Spring forums where I announced this solution.