Hibernate, Spring and multiple datasources (Part 3)

So, how to use the same connection for all the partitions by switching the catalog? And before that why should one go for that? To answer the later, there are lots of advantages in going for that,

  • You can reduce the number of connection pools
  • The number of session factories can be limited as the pools have been reduced.

The first thing we have to do is to make the DataSourceIdentifier return the catalog,

public abstract class DataSourceIdentifier {

	protected String dataSourceId;
	protected String dataSourceCatalog;
	
	public String getDataSourceId() {
		if(dataSourceId == null) {
			resolve();
		}
		return dataSourceId;
	}

	public String getDataSourceCatalog() {
		if(dataSourceCatalog == null) {
			resolve();
		}
		return dataSourceCatalog;
	}

	// Add the partitioning logic here
	public abstract void resolve();
}

Then we have create one session factory (assuming that all partitions reside in the same db instance otherwise create one per db instance). We have to create a custom session factory bean to make sure the correct catalog is set before the connection is used by the session.
public class CustomLocalSessionFactoryBean extends LocalSessionFactoryBean {
	
	@Override
	protected void postProcessConfiguration(Configuration config) throws HibernateException {
		String connectionProvider = config.getProperty(Environment.CONNECTION_PROVIDER);
		if (connectionProvider != null) {
			if (connectionProvider.equals(TransactionAwareDataSourceConnectionProvider.class.getName())) {
				connectionProvider = CustomTransactionAwareDataSourceConnectionProvider.class.getName();
			} else if (connectionProvider.equals(LocalJtaDataSourceConnectionProvider.class.getName())) {
				connectionProvider = CustomLocalJtaDataSourceConnectionProvider.class.getName();
			} else if (connectionProvider.equals(LocalDataSourceConnectionProvider.class.getName())) {
				connectionProvider = CustomLocalDataSourceConnectionProvider.class.getName();
			}
			// Set Spring-provided DataSource as Hibernate ConnectionProvider.
			config.setProperty(Environment.CONNECTION_PROVIDER, connectionProvider);
		}
		super.postProcessConfiguration(config);
	}
	
	@Override
	protected void afterSessionFactoryCreation() throws Exception {
		SessionFactoryImplementor implementor = (SessionFactoryImplementor) getSessionFactory();
		ConnectionProvider provider = implementor.getConnectionProvider();
		if(provider instanceof CustomConnectionProvider) {
			((CustomConnectionProvider)provider).setFactory(implementor);
		}
		super.afterSessionFactoryCreation();
	}



	public interface CustomConnectionProvider {
		
		public void setFactory(SessionFactory factory);
	}

	public static class CustomLocalDataSourceConnectionProvider extends LocalDataSourceConnectionProvider implements CustomConnectionProvider {
		
		private SessionFactoryImplementor factory;
		
		@Override
		public void setFactory(SessionFactory factory) {
			this.factory = (SessionFactoryImplementor) factory;
		}

		@Override
		public Connection getConnection() throws SQLException {
			Connection connection = super.getConnection();
			DataSourceIdentifier dataSourceId = ContextHolder.getDataSourceId(this.factory);
			if(dataSourceId != null && dataSourceId.getDataSourceCatalog() != null) {
				connection.setCatalog(dataSourceId.getDataSourceCatalog());
			}
			connection.setAutoCommit(false);
			return connection;
		}
		
	}

	// Repeat the same for CustomTransactionAwareDataSourceConnectionProvider and CustomLocalJtaDataSourceConnectionProvider as well
	
}

The session factory configuration will look like this,
<bean id="db0" class="CustomLocalSessionFactoryBean">
	<property name="hibernateProperties" ref="hibernateProperties" />
	<property name="mappingResources" ref="dbPartitionMappings"/>
	<property name="dataSource" ref="db0DataSource" />
</bean>

And that is it. We are ready to test this out. It has been working pretty good in my laptop :-) I would love to get back from you guys on optimizing the design or the issues that you find with this logic.

submit to reddit Digg!

Advertisement

9 Comments »

  1. Tejo said

    thats definitely cool trick, this will really become a reference for those who need multiple database support.
    but also quite dificult understand for spring – hibernate newbie like me, would you plz give us the code, so then we can comprehend the complete consept of your custom local session factory bean.(learning by sample, he he )

    • sgane said

      Tejo, I don’t have the source with me. I think whatever is there in the post should set an example of how to use this. Just try that out and ping me if you face any difficulties.

      • Arima said

        will it possible for you to share the xml configuration atleast.

      • sgane said

        I don’t understand how its going to help you… But yes we can share the mapping file, if you use spring in conjunction with hibernate.

  2. James said

    Do you know what affect (if any) this will have on Hibernate’s query caching?

    • sgane said

      Shouldn’t be an issue as cache will be maintained in the session factory which is the same for all the partitions in that db instance.

      In fact I have gone a step ahead and come up with a hibernate component that can be used for horizontal partitioning needs. Couldn’t find time to document it to make it public.

  3. [...] Hibernate, Spring and multiple datasources (Part 3) March 20096 comments 4 [...]

  4. ABO said

    Hi,

    thank’s a lot for the post, it’s quite help a couple of hours and a big head ache,
    my question is, I’m on a project which has to access three diffrent databases with the same schema, the project uses DBunit and Unitils for test, When I use the AbstractRoutingDataSource for dynamic Datasource routing, first test passes with no problem the secon (I mean the seccon withinthe same test class) doesn’t I have Integrity Exception when Unitils tries to clear the database. the only Exception raised is Caused by: java.sql.SQLException: Integrity constraint violation FKDBCF5D322FF73301 table: ABSTRACT_PARAMETRE, when ever I switch to a unique datasource configuration, my tests run correctly.

    for information, the clear action processed by unitils will try to do so by using directly “JDBC statements” on each table within the database.

    I suspect that somthing is locking my database , probably the hibernate session, and then Unitils is no more able to do clean on that database.

    Any clue ? “don’t tell me why we use Unitils, I’m asking every day the same question :)

    thank’s in advance

    • sgane said

      It will be difficult to answer that without looking at the code. I assume that you clean up the db after every test run. If my assumption is correct, I suspect that the clean operation is done on a different datasource than the one you intended to do.

      If you can post the code, I can see if I will be of any help here.

RSS feed for comments on this post · TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.