Spring整合Hibernate动态切换SessionFactory (切换数据库方言)

来源:互联网 发布:知乎怎么找匿名回答 编辑:程序博客网 时间:2024/05/22 12:38

1、定义全局切换SessionFactory的工具

package com.hoo.framework.spring.support;
      
 
      
/**
      
 * <b>function:</b> 多数据源
      
 * @author hoojo
      
 * @createDate 2013-9-27 上午11:36:57
      
 * @file CustomerContextHolder.java
      
 * @package com.hoo.framework.spring.support
      
 * @project SHMB
      
 * @blog http://blog.csdn.net/IBM_hoojo
      
 * @email hoojo_@126.com
      
 * @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();  
      
    }  
      
}

同样上面的静态变量和前一文章中介绍的一致,它需要和下面配置文件中的SessionFactory的key对应。

2、实现自己的SessionFactory

定义好接口

package com.hoo.framework.spring.support.core;
      
 
      
import org.hibernate.SessionFactory;
      
 
      
/**
      
 * <b>function:</b> 动态SessionFactory接口
      
 * @author hoojo
      
 * @createDate 2013-10-12 下午03:29:52
      
 * @file DynamicSessionFactory.java
      
 * @package com.hoo.framework.spring.support.core
      
 * @project SHMB
      
 * @blog http://blog.csdn.net/IBM_hoojo
      
 * @email hoojo_@126.com
      
 * @version 1.0
      
 */
      
public interface DynamicSessionFactory extends SessionFactory {
      
    
      
    public SessionFactory getHibernateSessionFactory();
      
}

 

实现接口

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> 动态数据源实现
      
 * @author hoojo
      
 * @createDate 2013-10-12 下午03:31:31
      
 * @file DynamicSessionFactoryImpl.java
      
 * @package com.hoo.framework.spring.support.core
      
 * @project SHMB
      
 * @blog http://blog.csdn.net/IBM_hoojo
      
 * @email hoojo_@126.com
      
 * @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> 重写这个方法,这里最关键
      
     * @author hoojo
      
     * @createDate 2013-10-18 上午10:45:25
      
     */
      
    @Override
      
    public SessionFactory getHibernateSessionFactory() {
      
        SessionFactory targetSessionFactory = targetSessionFactorys.get(CustomerContextHolder.getCustomerType());  
      
        if (targetSessionFactory != null) {  
      
            return targetSessionFactory;  
      
        } else if (defaultTargetSessionFactory != null) {  
      
            return defaultTargetSessionFactory;  
      
        }  
      
        return null;
      
    }
      
 
      
 
      
    @Override
      
    public void close() throws HibernateException {
      
        this.getHibernateSessionFactory().close();
      
    }
      
 
      
    @Override
      
    public boolean containsFetchProfileDefinition(String s) {
      
        return this.getHibernateSessionFactory().containsFetchProfileDefinition(s);
      
    }
      
 
      
    @Override
      
    public void evict(Class clazz) throws HibernateException {
      
        this.getHibernateSessionFactory().evict(clazz);
      
    }
      
 
      
    @Override
      
    public void evict(Class clazz, Serializable serializable) throws HibernateException {
      
        this.getHibernateSessionFactory().evict(clazz, serializable);
      
    }
      
 
      
    @Override
      
    public void evictCollection(String s) throws HibernateException {
      
        this.getHibernateSessionFactory().evictCollection(s);
      
    }
      
 
      
    @Override
      
    public void evictCollection(String s, Serializable serializable) throws HibernateException {
      
        this.getHibernateSessionFactory().evictCollection(s, serializable);
      
    }
      
 
      
    @Override
      
    public void evictEntity(String entity) throws HibernateException {
      
        this.getHibernateSessionFactory().evictEntity(entity);
      
    }
      
 
      
    @Override
      
    public void evictEntity(String entity, Serializable serializable) throws HibernateException {
      
        this.getHibernateSessionFactory().evictEntity(entity, serializable);
      
    }
      
 
      
    @Override
      
    public void evictQueries() throws HibernateException {
      
        this.getHibernateSessionFactory().evictQueries();        
      
    }
      
 
      
    @Override
      
    public void evictQueries(String queries) throws HibernateException {
      
        this.getHibernateSessionFactory().evictQueries(queries);        
      
    }
      
 
      
    @Override
      
    public Map<String, ClassMetadata> getAllClassMetadata() {
      
        return this.getHibernateSessionFactory().getAllClassMetadata();
      
    }
      
 
      
    @Override
      
    public Map getAllCollectionMetadata() {
      
        return this.getHibernateSessionFactory().getAllClassMetadata();
      
    }
      
 
      
    @Override
      
    public Cache getCache() {
      
        return this.getHibernateSessionFactory().getCache();
      
    }
      
 
      
    @Override
      
    public ClassMetadata getClassMetadata(Class clazz) {
      
        return this.getHibernateSessionFactory().getClassMetadata(clazz);
      
    }
      
 
      
    @Override
      
    public ClassMetadata getClassMetadata(String classMetadata) {
      
        return this.getHibernateSessionFactory().getClassMetadata(classMetadata);
      
    }
      
 
      
    @Override
      
    public CollectionMetadata getCollectionMetadata(String collectionMetadata) {
      
        return this.getHibernateSessionFactory().getCollectionMetadata(collectionMetadata);
      
    }
      
 
      
    @Override
      
    public Session getCurrentSession() throws HibernateException {
      
        return this.getHibernateSessionFactory().getCurrentSession();
      
    }
      
 
      
    @Override
      
    public Set getDefinedFilterNames() {
      
        return this.getHibernateSessionFactory().getDefinedFilterNames();
      
    }
      
 
      
    @Override
      
    public FilterDefinition getFilterDefinition(String definition) throws HibernateException {
      
        return this.getHibernateSessionFactory().getFilterDefinition(definition);
      
    }
      
 
      
    @Override
      
    public Statistics getStatistics() {
      
        return this.getHibernateSessionFactory().getStatistics();
      
    }
      
 
      
    @Override
      
    public TypeHelper getTypeHelper() {
      
        return this.getHibernateSessionFactory().getTypeHelper();
      
    }
      
 
      
    @Override
      
    public boolean isClosed() {
      
        return this.getHibernateSessionFactory().isClosed();
      
    }
      
 
      
    @Override
      
    public Session openSession() throws HibernateException {
      
        return this.getHibernateSessionFactory().openSession();
      
    }
      
 
      
    @Override
      
    public Session openSession(Interceptor interceptor) throws HibernateException {
      
        return this.getHibernateSessionFactory().openSession(interceptor);
      
    }
      
 
      
    @Override
      
    public Session openSession(Connection connection) {
      
        return this.getHibernateSessionFactory().openSession(connection);
      
    }
      
 
      
    @Override
      
    public Session openSession(Connection connection, Interceptor interceptor) {
      
        return this.getHibernateSessionFactory().openSession(connection, interceptor);
      
    }
      
 
      
    @Override
      
    public StatelessSession openStatelessSession() {
      
        return this.getHibernateSessionFactory().openStatelessSession();
      
    }
      
 
      
    @Override
      
    public StatelessSession openStatelessSession(Connection connection) {
      
        return this.getHibernateSessionFactory().openStatelessSession(connection);
      
    }
      
 
      
    @Override
      
    public 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;
      
    }
      
 
      
}

上面最重要的就是getHibernateSessionFactory重写这个方法,其他方法和原来实现的无异。重写这个方法后利用CustomerContextHolder动态设置SessionFactory类型就可以动态的切换SessionFactory。

3、动态的事务管理器,因为我们这里是动态切换SessionFactory,所以事务这块也需要动态切换SessionFactory来完成事务的操作。

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> 重写HibernateTransactionManager事务管理器,实现自己的动态的事务管理器
      
 * @author hoojo
      
 * @createDate 2013-10-12 下午03:54:02
      
 * @file DynamicTransactionManager.java
      
 * @package com.hoo.framework.spring.support.tx
      
 * @project SHMB
      
 * @blog http://blog.csdn.net/IBM_hoojo
      
 * @email hoojo_@126.com
      
 * @version 1.0
      
 */
      
public class DynamicTransactionManager extends HibernateTransactionManager {
      
 
      
    private static final long serialVersionUID = -4655721479296819154L;
      
    
      
    /** 
      
     * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getDataSource()
      
     * <b>function:</b> 重写
      
     * @author hoojo
      
     * @createDate 2013-10-12 下午03:55:24
      
     */
      
    @Override
      
    public DataSource getDataSource() {
      
        return SessionFactoryUtils.getDataSource(getSessionFactory());
      
    }
      
 
      
    /** 
      
     * @see org.springframework.orm.hibernate4.HibernateTransactionManager#getSessionFactory()
      
     * <b>function:</b> 重写
      
     * @author hoojo
      
     * @createDate 2013-10-12 下午03:55:24
      
     */
      
    @Override
      
    public SessionFactory getSessionFactory() {
      
        DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();  
      
        SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();  
      
        return hibernateSessionFactory;  
      
    }
      
}

这里主要重写getDataSource()/getSessionFactory()这两个方法,getSessionFactory方法是利用我们上面定义的接口来动态获取我们在上下文(CustomerContextHolder)中定义切换的SessionFactory对象。而getDataSource则是获得动态SessionFactory的DataSource,这里也不难理解。

4、至此,重写的接口和实现都完成,下面开始配置相关的代码

<?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/beans 
      
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 
      
    http://www.springframework.org/schema/aop 
      
    http://www.springframework.org/schema/aop/spring-aop-3.2.xsd 
      
    http://www.springframework.org/schema/tx  
      
    http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
      
    
      
    <!-- 配置c3p0数据源 -->
      
    <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&amp;characterEncoding=UTF-8&amp;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 配置sessionFactory,配置数据库连接,注入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>
      
                <!-- 链接释放策略 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 配置sessionFactory,配置数据库连接,注入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>
      
    
      
    <!-- 动态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>
      
 
      
    <!-- 自定义动态切换SessionFactory事务管理器,注入sessionFactory  -->
      
    <bean id="transactionManager" class="com.hoo.framework.spring.support.tx.DynamicTransactionManager">
      
        <property name="sessionFactory" ref="sessionFactory" />
      
    </bean>
      
    
      
    <!-- 配置事务的传播特性 -->
      
    <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>
      
    
      
    <!-- 配置那些类、方法纳入到事务的管理 -->
      
    <aop:config>
      
        <aop:pointcut expression="execution(* com.hoo.**.service.impl.*.*(..))" id="transactionManagerMethod"/>
      
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionManagerMethod" />
      
    </aop:config>
      
</beans>

配置也和我们之前的配置差不多,就是事务管理器部分注入的SessionFactory是我们自己定义的。

5、简单测试

@Test
      
public void testAdd() {
      
    // 主要就是这行代码 它才是完成SessionFactory的切换
      
    CustomerContextHolder.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();
      
    }
      
}

经过测试发现可以查询数据,利用hibernate查询分页也可以生成对应数据库的分页语句。同样在服务层的方法中添加完操作后,特意抛出一个异常,数据也能正常回滚操作,所以DynamicTransactionManager也是起到了该有的作用!

这里测试用例是手动切换CustomerContextHolder.setCustomerType的,但实际开发中我们还是得用Spring的Aop中的Interceptor进行切面编程,完成动态切换SessionFactory。

0 0