7

I am working on a project which uses spring + hibernate + mysql and c3p0 for connection pooling.

Currently the properties for the connection pool are loaded via properties defined outside the src. (eg: ${db_uname})

Everything starts fine when we create the spring bean.

It might so happen that the database to which we have connected is inaccessible for some reason, and we would like to switch hosts.

Need to implement a call back, where it is supposed to connect to the new host and re-initialize the pool

Any pointers on how to override the existing data source / connection pool gracefully would be of great help.

Here is how my spring config file looks like

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-3.0.xsd">

<!-- Component scans -->
<import resource="component-scans-1.xml" />
<import resource="component-scans-2.xml" />


<util:properties id="serviceManagerProperties" 
    location="classpath:servicemanagers.properties" />

<!-- Properties file -->
<context:property-placeholder location="classpath:database.config.properties,classpath:framework.properties" />

<!-- context:annotation-config / -->
<tx:annotation-driven transaction-manager="transactionManager" />

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
</bean>

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<context:mbean-server id="mbeanServer" />
<context:mbean-export server="mbeanServer" default-domain="a.b.c" />

<bean id="cacheManager" factory-method="getInstance"
    class="net.sf.ehcache.CacheManager" />

<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
    <constructor-arg ref="cacheManager" />
    <constructor-arg ref="mbeanServer" />
    <constructor-arg value="false" />
    <constructor-arg value="false" />
    <constructor-arg value="false" />
    <constructor-arg value="true" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaAdapter" />
    <property name="persistenceXmlLocation" value="classpath*:META-INF/framework-persistence.xml" />
    <property name="persistenceUnitName" value="PU-NAME" />
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.cache.use_query_cache">${hibernate.cache.use_query_cache}</prop>
            <prop key="hibernate.cache.use_second_level_cache">${hibernate.cache.use_second_level_cache}</prop>
            <prop key="hibernate.cache.region.factory_class">
                org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory
            </prop>

            <prop key="hibernate.ejb.cfgfile">hibernate.cfg.xml</prop>
            <prop key="hibernate.generate_statistics">true</prop>


            <!-- CONNECTION SETTINGS -->
            <prop key="hibernate.connection.driver_class">
                com.mysql.jdbc.Driver
            </prop>
            <prop key="hibernate.connection.url">
                jdbc:mysql://${dbhost}/${dbschema}?zeroDateTimeBehavior=convertToNull&amp;useUnicode=true&amp;characterEncoding=UTF-8
            </prop>
            <prop key="hibernate.connection.username">${dbuser}</prop>
            <prop key="hibernate.connection.password">${dbpass}</prop>

            <!-- CONNECTION POOLING -->
            <prop key="hibernate.connection.provider_class">
                org.hibernate.connection.C3P0ConnectionProvider
            </prop>
            <prop key="hibernate.c3p0.maxPoolSize">${hibernate.c3p0.maxSize}</prop>
            <prop key="hibernate.c3p0.minPoolSize">${hibernate.c3p0.minSize}</prop>
            <prop key="hibernate.c3p0.acquireIncrement">${hibernate.c3p0.acquireIncrement}</prop>
            <prop key="hibernate.c3p0.idleConnectionTestPeriod">${hibernate.c3p0.idleTestPeriod}</prop>
            <prop key="hibernate.c3p0.maxStatements">${hibernate.c3p0.maxStatements}</prop>
            <prop key="hibernate.c3p0.timeout">${hibernate.c3p0.timeout}</prop>

        </props>
    </property>
</bean>

<bean id="jpaAdapter"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />

Assuming the database schema is right, lets say i get the database credentials and the host information in the event, i need to 're-set' the connection pool.

Abhilash L L
  • 366
  • 2
  • 7
  • 16

3 Answers3

3

AbstractRoutingDataSource is a good choice.

Xml or annotation used like this:

<bean id="ds1" class="..c3p0.DataSource">
    ...
</bean>

<bean id="ds2" class="..c3p0.DataSource">
    ...
</bean>

<bean id="dataSource" class="..xxx.RoutingDataSource">
   <property name="targetDataSources">
      <map key-type="java.lang.String">
         <entry key="ds1" value-ref="ds1"/>
         <entry key="ds2" value-ref="ds2"/>             
      </map>
   </property>
   <property name="defaultTargetDataSource" ref="ds1"/>
</bean>



<bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource" /> 
...
</bean>

Then build a class to determine current datasoruce.

public class RoutingDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();

    protected Object determineCurrentLookupKey()
    {
        return holder.get();
    }

    public static void clear(){
        holder.remove();
    }

    public static void setDataSourceKey(String key){
        holder.set(key);
    }

}

By the way, the 'try-finally' statement is boring!

try{
    RoutingDataSource.setDataSourceKey("ds1");
    myDao.doXXX();
}finally{
    RoutingDataSource.clear();
}
imxylz
  • 7,847
  • 4
  • 28
  • 25
2
<beans:bean id="dataSource"
    class="org.springframework.aop.framework.ProxyFactoryBean">
    <beans:property name="targetSource" ref="swappableDataSource" />
</beans:bean>

<beans:bean id="dummyDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close" />

<beans:bean name="swappableDataSource"
    class="org.springframework.aop.target.HotSwappableTargetSource">
    <beans:constructor-arg ref="dummyDataSource" />
</beans:bean>

and some where in your code you can do this.

@Autowired
HotSwappableTargetSource swapable;

public void changeDatasource() throws Exception
{
            swapable.swap(createNewSource();

}


ComboPooledDataSource createNewSource() throws Exception {
    ComboPooledDataSource ds2 = new ComboPooledDataSource();
    ds2.setJdbcUrl(url);
    ds2.setDriverClass(driver);
    ds2.setUser(username);
    ds2.setPassword(password);


    return ds2;
}
arshid dar
  • 1,355
  • 2
  • 15
  • 23
0

If you are asking for multiple databases connections ..it is possible with hibernate by creating multiple hibernate.cfg.file 's which is some what inappropriate

By following line you can acheive that .

SessionFactory sf = new Configuration().configure("somename.cfg.xml").buildSessionFactory();

When your primary connection not established you have load another hibernate configuration ...otherwise is wont possible .

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307