Hibernate::
beyond "hello world"

 


Download sources

Contact us::

Hibernate Session Virtualization.

Last update: Oct-12-2005

Lets consider this stack: an operating system like GNU-Linux that acts as hardware virtualization layer, and than we put a JVM atop of the OS, and then J2EE has adds another layer of virtualization in the form of EJB- or Web- container, and now we are talking about adding Hibernate Session virtualization. Huh?

Sounds like too much of virtualization, doesn't it?

Well, sometimes we need it. Lets consider couple of scenarios:

  • Suppose that we host an application that provides access to multiple identical databases. Perhaps one database per company and we want all the users from a company worked with its database only;
  • Or we have requirement that every user must use own connection to the back end database (trigger based audit and logging perhaps), no problem for standalone application, but can be tricky for web applications;

Sometimes it is possible to configure multiple hibernate session factories statically, but sometimes it might be difficult or inefficient.

Hibernate provides us with the hook to achieve our goal easier: we can configure just one session factory and then use sessionFactory.getSession( ourConnection ). This will cause Hibernate to create lightweight session object that will operate on the provided connection. The provided connection can be created or borrowed from an appropriate connection pool according to our rules. For example: we could create/reuse connection for the user, or define a database connection based on the current user name, there are no limits.

Lets look at the diagram below that depicts a web application that connects every user to its own database.

There is nothing complicated in the approach, its beauty is the simplicity of the implementation. We will simply supply Hibernate with a custom Data Source implementation that is aware of its environment in addition to the initial configuration parameters.

ContextAwareDSProxy.java
public class ContextAwareDSProxy implements DataSource { 
16    
17     private String dbUrl; 
18     private PrintWriter log; 
19     private int loginTimeout; 
20    
21     public void setDriverClassName( String  driverClassName ) 
22       throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException{ 
23       DriverManager.registerDriver( ( Driver ) Class.forName( driverClassName).newInstance() ); 
24     } 
25    
26     public void setDbUrl( String dbUrl ){ 
27       this.dbUrl = dbUrl; 
28     } 
29    
30     public Connection getConnection() throws SQLException{ 
31       if(CallEnv.getUserName() == null &&  CallEnv.getUserPassword() == null ){ 
32         throw new SQLException( "Inappropriate call environment: user name or password is null"); 
33       } 
34       System.out.println( "Returning connection for::" + CallEnv.getUserName() + " for thread::" 
                              + Thread.currentThread() ); 
35       return DriverManager.getConnection( dbUrl, CallEnv.getUserName(), CallEnv.getUserPassword()); 
36     } 
37    
38     public Connection getConnection( String string, String string1 ) throws SQLException{ 
39       return getConnection( ); 
40     } 
41    
42     public PrintWriter getLogWriter() throws SQLException{ 
43       return log; 
44     } 
45    
46     public void setLogWriter( PrintWriter printWriter ) throws SQLException{ 
47       log =  printWriter; 
48     } 
49    
50     public void setLoginTimeout( int i ) throws SQLException{ 
51       loginTimeout = i; 
52     } 
53    
54     public int getLoginTimeout() throws SQLException{ 
55       return loginTimeout; 
56     } 
57   } 
58   

This particular example returns a connection to the same database based on user credentials, but we could just use user name to construct a different DB URL, or define the necessary DB URL from a mapping of users to databases.

That is it, when we run a test client we can see that each thread uses its own database connection.

run-class:
     [java] 0    [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader  
	 - Loading XML bean definitions from class path resource [
     com/sourcelabs/hibernate/bhw/vsession/vsession.spring.xml]
     [java] 266  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  
	 - Bean factory for application context [or
     g.springframework.context.support.ClassPathXmlApplicationContext;hashCode=11468767]
: org.springframework.beans.factory.support.DefaultListableBeanFactory defining beans  
     [propertyConfigurer,dataSource,hSessionFactory,appTxManager,allTxAttrDS,
     businessLogicImpl,businessLogic]; root of BeanFactory hierarchy
     [java] 284  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  
	 - 7 beans defined in applica
tion context [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=11468767]
     [java] 343  [main] INFO  org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
	   - Loading properties fil
e from class path resource [com/sourcelabs/hibernate/bhw/vsession/jdbc.properties]
     [java] 351  [main] INFO  org.springframework.core.CollectionFactory  
	 - JDK 1.4+ collections available
     [java] 373  [main] INFO  org.springframework.core.CollectionFactory  
	 - Commons Collections 3.x available
     [java] 380  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  
	 - Unable to locate MessageSource with name 'messageSource': us
      ing default [org.springframework.context.support.DelegatingMessageSource@288051]
     [java] 383  [main] INFO  org.springframework.context.support.ClassPathXmlApplicationContext  
	 - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': 
using default  [org.springframework.context.event.SimpleApplication
EventMulticaster@6cb8]
     [java] 384  [main] INFO  org.springframework.beans.factory.support.DefaultListableBeanFactory  
	 - Pre-instantiating singletons in factory 
 [org.springframework.beans.factory.support.DefaultListableBeanFactory 
defining beans [propertyConfigurer,dataSource,hSessionFactory,appTxManager,allTxAttrDS,
businessLogicImpl,businessLogic]; 
root of BeanFactory hierarchy]
     [java] 918  [main] INFO  org.springframework.orm.hibernate3.LocalSessionFactoryBean  
	 - Building new Hibernate SessionFactory     [ja
va] 1045 [main] ERROR org.hibernate.util.JDBCExceptionReporter  - Inappropriate call environment:
 user name or password is null
     [java] 1459 [main] WARN  net.sf.ehcache.config.Configurator  
     - No configuration found. Configuring ehcache from ehcache - failsafe.xml  
      found in the classpath: jar:file:/home/kosta/.m2/repository/net/sf/ehcache
/ehcache/1.1-1/ehcache-1.1-1.jar!/ehcache-failsafe.xml
     [java] 1784 [main] INFO  org.springframework.orm.hibernate3.HibernateTransactionManager  
	 - Using DataSource [com.sourcela
bs.hibernate.bhw.vsession.ContextAwareDSProxy@8046f4] of Hibernate SessionFactory 
for HibernateTransactionManager
     [java] 1811 [main] INFO  org.springframework.aop.framework.DefaultAopProxyFactory  
	 - CGLIB2 available: proxyTargetClass f
eature enabled
     [java] 1982 [Thread-0] INFO  org.springframework.jdbc.datasource.JdbcTransactionObjectSupport  
	 - JDBC 3.0 Savepoint class
 is available
     [java] Returning connection for::dbuser for thread::Thread[Thread-0,5,main]
     [java] Returning connection for::dbuser2 for thread::Thread[Thread-1,5,main]
     [java] Returning connection for::dbuser2 for thread::Thread[Thread-1,5,main]
     [java] Returning connection for::dbuser for thread::Thread[Thread-0,5,main]
     [java] Returning connection for::dbuser2 for thread::Thread[Thread-1,5,main]
     [java] Returning connection for::dbuser for thread::Thread[Thread-0,5,main]
     [java] Returning connection for::dbuser for thread::Thread[Thread-0,5,main]
     [java] Returning connection for::dbuser2 for thread::Thread[Thread-1,5,main]
     [java] Returning connection for::dbuser for thread::Thread[Thread-0,5,main]
     [java] Returning connection for::dbuser2 for thread::Thread[Thread-1,5,main]