Implementation code
1, define the overall tool of the switching SessionFactory
package com.hoo.framework.spring.support;/*** <B> function: </ b> multiple data sources* @author hoojo* @CreateDate 2013-9-27 11:36:57 AM* @file CustomerContextHolder.java* @package com.hoo.framework.spring.support* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/public abstract class CustomerContextHolder {public final static String SESSION_FACTORY_MYSQL = "mysql";public final static String SESSION_FACTORY_ORACLE = "oracle";private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();public static void setCustomerType(String customerType) {contextHolder.set(customerType);}public static String getCustomerType() {return contextHolder.get();}public static void clearCustomerType() {contextHolder.remove();}}
Also consistent with the above static variables and described in the previous article, it needs to correspond to the following configuration file and key in the SessionFactory.
2, to realize their SessionFactory
Well defined interfaces
package com.hoo.framework.spring.support.core;import org.hibernate.SessionFactory;/*** <B> function: </ b> Dynamic Interface SessionFactory* @author hoojo* @CreateDate 2013-10-12 03:29:52 pm* @file DynamicSessionFactory.java* @package com.hoo.framework.spring.support.core* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/public interface DynamicSessionFactory extends SessionFactory {public SessionFactory getHibernateSessionFactory();}
Implement an interface
package com.hoo.framework.spring.support.core;import java.io.Serializable;import java.sql.Connection;import java.util.Map;import java.util.Set;import javax.naming.NamingException;import javax.naming.Reference;import org.hibernate.Cache;import org.hibernate.HibernateException;import org.hibernate.Interceptor;import org.hibernate.SessionFactory;import org.hibernate.StatelessSession;import org.hibernate.TypeHelper;import org.hibernate.classic.Session;import org.hibernate.engine.FilterDefinition;import org.hibernate.metadata.ClassMetadata;import org.hibernate.metadata.CollectionMetadata;import org.hibernate.stat.Statistics;import com.hoo.framework.spring.support.CustomerContextHolder;/*** <B> function: </ b> dynamic data sources to achieve* @author hoojo* @CreateDate 2013-10-12 03:31:31 pm* @file DynamicSessionFactoryImpl.java* @package com.hoo.framework.spring.support.core* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/@SuppressWarnings({ "unchecked", "deprecation" })public class DynamicSessionFactoryImpl implements DynamicSessionFactory {private static final long serialVersionUID = 5384069312247414885L;private Map<Object, SessionFactory> targetSessionFactorys;private SessionFactory defaultTargetSessionFactory;/*** @see com.hoo.framework.spring.support.core.DynamicSessionFactory#getHibernateSessionFactory()* <B> function: </ b> override this method, where the most critical* @author hoojo* @CreateDate 2013-10-18 10:45:25 AM*/@Overridepublic SessionFactory getHibernateSessionFactory() {SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());if (targetSessionFactory != null) {return targetSessionFactory;} else if (defaultTargetSessionFactory != null) {return defaultTargetSessionFactory;}return null;}@Overridepublic void close() throws HibernateException {this.getHibernateSessionFactory().close();}@Overridepublic boolean containsFetchProfileDefinition(String s) {return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);}@Overridepublic void evict(Class clazz) throws HibernateException {this.getHibernateSessionFactory().evict(clazz);}@Overridepublic void evict(Class clazz, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evict(clazz, serializable);}@Overridepublic void evictCollection(String s) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s);}@Overridepublic void evictCollection(String s, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictCollection(s, serializable);}@Overridepublic void evictEntity(String entity) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity);}@Overridepublic void evictEntity(String entity, Serializable serializable) throws HibernateException {this.getHibernateSessionFactory().evictEntity(entity, serializable);}@Overridepublic void evictQueries() throws HibernateException {this.getHibernateSessionFactory().evictQueries();}@Overridepublic void evictQueries(String queries) throws HibernateException {this.getHibernateSessionFactory().evictQueries(queries);}@Overridepublic Map<String, ClassMetadata> getAllClassMetadata() {return this.getHibernateSessionFactory().getAllClassMetadata();}@Overridepublic Map getAllCollectionMetadata() {return this.getHibernateSessionFactory().getAllClassMetadata();}@OverrideCache getCache public () {return this.getHibernateSessionFactory().getCache();}@Overridepublic ClassMetadata getClassMetadata(Class clazz) {return this.getHibernateSessionFactory().getClassMetadata(clazz);}@Overridepublic ClassMetadata getClassMetadata(String classMetadata) {return this.getHibernateSessionFactory().getClassMetadata(classMetadata);}@Overridepublic CollectionMetadata getCollectionMetadata(String collectionMetadata) {return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata);}@Overridepublic Session getCurrentSession() throws HibernateException {return this.getHibernateSessionFactory().getCurrentSession();}@Overridepublic Set getDefinedFilterNames() {return this.getHibernateSessionFactory().getDefinedFilterNames();}@Overridepublic FilterDefinition getFilterDefinition(String definition) throws HibernateException {return this.getHibernateSessionFactory().getFilterDefinition(definition);}@Overridepublic Statistics getStatistics() {return this.getHibernateSessionFactory().getStatistics();}@Overridepublic TypeHelper getTypeHelper() {return this.getHibernateSessionFactory().getTypeHelper();}@Overridepublic boolean isClosed() {return this.getHibernateSessionFactory().isClosed();}@Overridepublic Session openSession() throws HibernateException {return this.getHibernateSessionFactory().openSession();}@Overridepublic Session openSession(Interceptor interceptor) throws HibernateException {return this.getHibernateSessionFactory().openSession(interceptor);}@Overridepublic Session openSession(Connection connection) {return this.getHibernateSessionFactory().openSession(connection);}@Overridepublic Session openSession(Connection connection, Interceptor interceptor) {return this.getHibernateSessionFactory().openSession(connection, interceptor);}@Overridepublic StatelessSession openStatelessSession() {return this.getHibernateSessionFactory().openStatelessSession();}@Overridepublic StatelessSession openStatelessSession(Connection connection) {return this.getHibernateSessionFactory().openStatelessSession(connection);}@Overridepublic Reference getReference() throws NamingException {return this.getHibernateSessionFactory().getReference();}public void setTargetSessionFactorys(Map<Object, SessionFactory> targetSessionFactorys) {this.targetSessionFactorys = targetSessionFactorys;}public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) {this.defaultTargetSessionFactory = defaultTargetSessionFactory;}}
The most important of the above is getHibernateSessionFactory override this method, and other methods to achieve the original is no different. CustomerContextHolder provided using dynamic type SessionFactory SessionFactory can dynamically switch after override this method.
3, dynamic transaction manager, because we are here SessionFactory dynamic switching, so this transaction also need to dynamically switch operating SessionFactory to complete the transaction.
package com.hoo.framework.spring.support.tx;import javax.sql.DataSource;import org.hibernate.SessionFactory;import org.springframework.orm.hibernate3.HibernateTransactionManager;import org.springframework.orm.hibernate3.SessionFactoryUtils;import com.hoo.framework.spring.support.core.DynamicSessionFactory;/*** <B> function: </ b> rewrite HibernateTransactionManager transaction manager, realize their dynamic transaction manager* @author hoojo* @CreateDate 2013-10-12 03:54:02 pm* @file DynamicTransactionManager.java* @package com.hoo.framework.spring.support.tx* @project SHMB* @blog http://blog.csdn.net/IBM_hoojo* @email [email protected]* @version 1.0*/public class DynamicTransactionManager extends HibernateTransactionManager {private static final long serialVersionUID = -4655721479296819154L;/*** @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource()* <B> function: </ b> rewritable* @author hoojo* @CreateDate 2013-10-12 03:55:24 pm*/@Overridepublic DataSource getDataSource() {return SessionFactoryUtils.getDataSource(getSessionFactory());}/*** @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory()* <B> function: </ b> rewritable* @author hoojo* @CreateDate 2013-10-12 03:55:24 pm*/@Overridepublic SessionFactory getSessionFactory() {DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();return hibernateSessionFactory;}}
Here major rewrite getDataSource () / getSessionFactory () these two methods, getSessionFactory our method is to use an interface as defined above to obtain the SessionFactory we define dynamic switching in the context of (CustomerContextHolder) in. The getDataSource is obtained SessionFactory dynamic of DataSource, not difficult to understand here.
4. Thus, the interface and implementation rewriting are completed, the following configuration related to the start code
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.2.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.2.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.2.xsd "><! - c3p0 configuration data source -><bean id="dataSourceOracle" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="${datasource.driver}"/><property name="jdbcUrl" value="${datasource.url}"/><property name="user" value="${datasource.username}"/><property name="password" value="${datasource.password}"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><bean id="dataSourceMySQL" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"><property name="driverClass" value="com.mysql.jdbc.Driver"/><property name="jdbcUrl" value="jdbc:mysql://172.31.108.178:3306/world?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull"/><property name="user" value="root"/><property name="password" value="jp2011"/><property name="acquireIncrement" value="${c3p0.acquireIncrement}"/><property name="initialPoolSize" value="${c3p0.initialPoolSize}"/><property name="minPoolSize" value="${c3p0.minPoolSize}"/><property name="maxPoolSize" value="${c3p0.maxPoolSize}"/><property name="maxIdleTime" value="${c3p0.maxIdleTime}"/><property name="idleConnectionTestPeriod" value="${c3p0.idleConnectionTestPeriod}"/><property name="maxStatements" value="${c3p0.maxStatements}"/><property name="numHelperThreads" value="${c3p0.numHelperThreads}"/><property name="preferredTestQuery" value="${c3p0.preferredTestQuery}"/><property name="testConnectionOnCheckout" value="${c3p0.testConnectionOnCheckout}"/></bean><-! Annotation the sessionFactory configuration, the connection configuration database, configuration database injection hibernate -><bean id="mySQLSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="dataSourceMySQL"/><property name="packagesToScan" value="com.hoo.**.mysqlentity"/><property name="annotatedClasses"><array><value>com.hoo.common.entity.IDGenerator</value></array></property><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop><! - link release strategy on_close | after_transaction | after_statement | auto -><prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop></props></property></bean><-! Annotation the sessionFactory configuration, the connection configuration database, configuration database injection hibernate -><bean id="oracleSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"><property name="dataSource" ref="dataSourceOracle"/><property name="packagesToScan" value="com.hoo.**.entity"/><property name="hibernateProperties"><props><prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop><prop key="hibernate.connection.release_mode">after_transaction</prop><prop key="hibernate.show_sql">true</prop><prop key="hibernate.format_sql">true</prop><!--prop key="hibernate.hbm2ddl.auto">update</prop--></props></property></bean><! - Dynamic SessionFactory -><bean id="sessionFactory" class="com.hoo.framework.spring.support.core.DynamicSessionFactoryImpl"><property name="defaultTargetSessionFactory" ref="oracleSessionFactory"/><property name="targetSessionFactorys"><map><entry value-ref="oracleSessionFactory" key="oracle"/><entry value-ref="mySQLSessionFactory" key="mysql"/></map></property></bean><! - custom dynamic switching SessionFactory transaction manager, injection sessionFactory -><bean id="transactionManager" class="com.hoo.framework.spring.support.tx.DynamicTransactionManager"><property name="sessionFactory" ref="sessionFactory" /></bean><! - Configure the transaction propagation characteristics -><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="edit*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="remove*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="execute*" propagation="REQUIRED" rollback-for="java.lang.Exception"/><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><! - Configure those classes, methods incorporated into the management of affairs -><aop:config><aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/><aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" /></aop:config></beans>
Configuration and also configure our previous similar transaction manager is part of the injected SessionFactory is our own definition.
5, simple test
@Testpublic void testAdd() {// This is the main line of code it is completed switch the SessionFactoryCustomerContextHolder.setCustomerType(CustomerContextHolder.SESSION_FACTORY_MYSQL);DeviceInfo entity = new DeviceInfo();entity.setSbbh(System.currentTimeMillis() + "");entity.setIpdz("my ip address2");entity.setJd(1234);try {service.add(entity);} catch (Exception e) {e.printStackTrace ();}}
Been tested and found to query data using hibernate query pagination pagination statement may generate a corresponding database. After you finish adding the same method of operation in the service layer, deliberately throws an exception, the data can be rolled back to normal operation , so DynamicTransactionManager also played a role in the others!
Here is my test cases manually switch CustomerContextHolder.setCustomerType, but we still have to use the actual development of the Spring Aop be cut Interceptor program, complete dynamic switching SessionFactory. Previous article Spring3.3 integration Hibernate3, MyBatis3.2 configure multiple data sources / data source dynamic switching method already mentioned (the reader can refer to this blog post in the second section of 3,7 DataSourceMethodInterceptor MultipleDataSourceInterceptor), not here then repeat!
Third, the problems caused by
If the above is achieved in the case of improper use, it is there may be some problems in the actual development!
Question 1 is normally the transaction is completed in this layer Service, this should be no objection. If that is the case, the problem appeared. And usually we will call Service in a way only the view layer in MVC in to finish processing all current operations. If the Service in simultaneous operation dbA, dbB two databases, transaction commit of the transaction which is the database to use it? So we put the database on a different operating method, there will be a problem transaction, unless the transaction can be rolled back two databases at the same time!
Roughly scenarios are: Service operation in add4Oracle Oracle database, Service of add4MySQL operations MySQL database, and finally the Service in add4Oracle, add4MySQL method into a method of operation, the business MVC view layer of control call Service in operation. Like this case, if one method add4Oracle or add4MySQL methods abnormal, you need to put two database transactions are rolled back.
The solution is a method in the Service or Dao is the simultaneous operation of two databases, transaction control done manually. Unusual to be rolled back, no exceptions will all submit, can only sacrificing.
Problem 2 is to use the interceptor can not switch a method of operation may be two databases, for example, a service query method, i.e., in need query the MySQL, need to query Oracle. Then the solution is to call the current service of query4Oracle and query4Oracle in query methods, or can take advantage of that to go around interceptor dynamically switching.