您的位置:首页 > 编程语言 > Java开发

Spring4+hibernate4+多数据源动态datasource+异构数据库

2016-03-30 00:00 585 查看
摘要: Spring4+hibernate4(SSH)+多数据源的动态datasource+多数据库(异构数据库)的动态sessionfactory

有台服务器上安装了Sqlserver和Oracle,其中Sqlserver上面有数据库A、B、C,Oracle上面有数据库D。实现方式如下:

1、新建一个类DBContextHolder,通过两个static的ThreadLocal<String>属性存放当前要使用的dataSource的别名和sessionFactory的别名。

package com.util.db;

public class DBContextHolder{
public static final String DATASOURCE_A = "dataSourceA";
public static final String DATASOURCE_B = "dataSourceB";
public static final String DATASOURCE_C = "dataSourceC";
public static final String DATASOURCE_D = "dataSourceD";
public final static String SESSION_FACTORY_SQLSERVER = "sqlserver";
public final static String SESSION_FACTORY_ORACLE = "oracle";

private static final ThreadLocal<String> dataSourceContextHolder = new ThreadLocal<String>();
private static final ThreadLocal<String> sessionFactoryContextHolder = new ThreadLocal<String>();

public static void setDataSourceType(String type) {
dataSourceContextHolder.set(type);
}
public static String getDataSourceType() {
return dataSourceContextHolder.get();
}
public static void clearDataSourceType() {
dataSourceContextHolder.remove();
}

public static void setSessionFactoryType(String type) {
sessionFactoryContextHolder.set(type);
}
public static String getSessionFactoryType() {
return sessionFactoryContextHolder.get();
}
public static void clearSessionFactoryType() {
sessionFactoryContextHolder.remove();
}
}


2、新建一个类,继承抽象类AbstractRoutingDataSource。这个抽象类是Spring为动态切换数据源准备的。要使用这个抽象类,一个是要重写determineCurrentLookupKey方法,告诉它当前要用哪个数据源;一是在Spring的配置文件中将几个数据源注入到targetDataSources属性中。本文中,该属性的形式为:HashMap<String,DataSource>。

package com.util.db;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDataSourceType();
}
}


3、参照步骤2,写一个动态切换数据源的工具类。关键有几点:

a、定义属性Map<Object, SessionFactory> targetSessionFactorys,通过Spring注入几个SessionFactory;

b、实现方法public SessionFactory getHibernateSessionFactory(),根据DBContextHolder中存放的当前sessionFactory别名来从targetSessionFactorys属性中取出对应的SessionFactory;

c、实现SessionFactory,重写其所有方法,将原本直接调用session的地方,改为getHibernateSessionFactory()。

package com.util.db;

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.Session;
import org.hibernate.SessionBuilder;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.hibernate.StatelessSessionBuilder;
import org.hibernate.TypeHelper;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.metadata.CollectionMetadata;
import org.hibernate.stat.Statistics;

/**
* Created by Administrator on 16-3-29.
*/
@SuppressWarnings({ "unchecked", "deprecation" })
public class DynamicSessionFactory implements SessionFactory {
private static final long serialVersionUID = 156487979415646L;
private Map<Object, SessionFactory> targetSessionFactorys;
private SessionFactory defaultTargetSessionFactory;

public void setTargetSessionFactorys(Map<Object, SessionFactory> targetSessionFactorys) {
this.targetSessionFactorys = targetSessionFactorys;
}

public void setDefaultTargetSessionFactory(SessionFactory defaultTargetSessionFactory) {
this.defaultTargetSessionFactory = defaultTargetSessionFactory;
}

@Override
public SessionFactoryOptions getSessionFactoryOptions() {
return getHibernateSessionFactory().getSessionFactoryOptions();
}

@Override
public SessionBuilder withOptions() {
return getHibernateSessionFactory().withOptions();
}

@Override
public Session openSession() throws HibernateException {
return getHibernateSessionFactory().openSession();
}

@Override
public Session getCurrentSession() throws HibernateException {
return getHibernateSessionFactory().getCurrentSession();
}

@Override
public StatelessSessionBuilder withStatelessOptions() {
return getHibernateSessionFactory().withStatelessOptions();
}

@Override
public StatelessSession openStatelessSession() {
return getHibernateSessionFactory().openStatelessSession();
}

@Override
public StatelessSession openStatelessSession(Connection connection) {
return getHibernateSessionFactory().openStatelessSession(connection);
}

@Override
public ClassMetadata getClassMetadata(
@SuppressWarnings("rawtypes") Class entityClass) {
return getHibernateSessionFactory().getClassMetadata(entityClass);
}

@Override
public ClassMetadata getClassMetadata(String entityName) {
return getHibernateSessionFactory().getClassMetadata(entityName);
}

@Override
public CollectionMetadata getCollectionMetadata(String roleName) {
return getHibernateSessionFactory().getCollectionMetadata(roleName);
}

@Override
public Map<String, ClassMetadata> getAllClassMetadata() {
return getHibernateSessionFactory().getAllClassMetadata();
}

@SuppressWarnings("rawtypes")
@Override
public Map getAllCollectionMetadata() {
return getHibernateSessionFactory().getAllCollectionMetadata();
}

@Override
public Statistics getStatistics() {
return getHibernateSessionFactory().getStatistics();
}

@Override
public void close() throws HibernateException {
getHibernateSessionFactory().close();
}

@Override
public boolean isClosed() {
return getHibernateSessionFactory().isClosed();
}

@Override
public Cache getCache() {
return getHibernateSessionFactory().getCache();
}

@SuppressWarnings({ "deprecation", "rawtypes" })
@Override
public void evict(Class persistentClass) throws HibernateException {
getHibernateSessionFactory().evict(persistentClass);
}

@SuppressWarnings({ "deprecation", "rawtypes" })
@Override
public void evict(Class persistentClass, Serializable id)
throws HibernateException {
getHibernateSessionFactory().evict(persistentClass, id);
}

@SuppressWarnings("deprecation")
@Override
public void evictEntity(String entityName) throws HibernateException {
getHibernateSessionFactory().evictEntity(entityName);
}

@SuppressWarnings("deprecation")
@Override
public void evictEntity(String entityName, Serializable id)
throws HibernateException {
getHibernateSessionFactory().evictEntity(entityName, id);
}

@SuppressWarnings("deprecation")
@Override
public void evictCollection(String roleName) throws HibernateException {
getHibernateSessionFactory().evictCollection(roleName);
}

@SuppressWarnings("deprecation")
@Override
public void evictCollection(String roleName, Serializable id)
throws HibernateException {
getHibernateSessionFactory().evictCollection(roleName, id);
}

@SuppressWarnings("deprecation")
@Override
public void evictQueries(String cacheRegion) throws HibernateException {
getHibernateSessionFactory().evictQueries(cacheRegion);
}

@SuppressWarnings("deprecation")
@Override
public void evictQueries() throws HibernateException {
getHibernateSessionFactory().evictQueries();
}

@SuppressWarnings("rawtypes")
@Override
public Set getDefinedFilterNames() {
return getHibernateSessionFactory().getDefinedFilterNames();
}

@Override
public FilterDefinition getFilterDefinition(String filterName)
throws HibernateException {
return getHibernateSessionFactory().getFilterDefinition(filterName);
}

@Override
public boolean containsFetchProfileDefinition(String name) {
return getHibernateSessionFactory()
.containsFetchProfileDefinition(name);
}

@Override
public TypeHelper getTypeHelper() {

return getHibernateSessionFactory().getTypeHelper();
}

@Override
public Reference getReference() throws NamingException {
return getHibernateSessionFactory().getReference();
}

public SessionFactory getHibernateSessionFactory() {
SessionFactory targetSessionFactory = targetSessionFactorys
.get(DBContextHolder.getSessionFactoryType());
if (targetSessionFactory != null) {
return targetSessionFactory;
} else if (defaultTargetSessionFactory != null) {
return defaultTargetSessionFactory;
}
return null;
}
}


4、新建HibernateTransactionManager的子类DynamicTransactionManager,重写getSessionFactory方法,不然取出来的不是SessionFactory,而是DynamicSessionFactory。

package com.util.db;

import org.springframework.orm.hibernate4.HibernateTransactionManager;
import javax.sql.DataSource;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate4.SessionFactoryUtils;

public class DynamicTransactionManager extends HibernateTransactionManager {

private static final long serialVersionUID = 2591641126163504954L;

@Override
public DataSource getDataSource() {
return SessionFactoryUtils.getDataSource(getSessionFactory());
}

@Override
public SessionFactory getSessionFactory() {
DynamicSessionFactory dynamicSessionFactory = (DynamicSessionFactory) super.getSessionFactory();
SessionFactory hibernateSessionFactory = dynamicSessionFactory.getHibernateSessionFactory();
return hibernateSessionFactory;
}

}


5、配置Spring

a、分别配置4个DataSource

<bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<!-- 设置JDBC驱动名称 -->
<property name="driverClass" value="${jdbc.driver}" />
<!-- 设置JDBC连接URL -->
<property name="jdbcUrl" value="${jdbc.url1}" />
<!-- 设置数据库用户名 -->
<property name="user" value="${jdbc.username}" />
<!-- 设置数据库密码 -->
<property name="password" value="${jdbc.password}" />
<!-- 设置连接池初始值 -->
<property name="initialPoolSize" value="5" />
</bean>


b、配置动态DataSource

<bean id="dynamicDataSource" class="com.util.db.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSourceA" key="dataSourceA" />
<entry value-ref="dataSourceB" key="dataSourceB" />
<entry value-ref="dataSourceC" key="dataSourceC" />
<entry value-ref="dataSourceD" key="dataSourceD" />
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceSmart">
</property>
</bean>


c、分别配置两个SessionFactory,其中DataSource引用dynamicDataSource

<bean id="sqlServerSessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dynamicDataSource" />
<!-- hibernate的相关属性配置 -->
<property name="hibernateProperties">
<props>
<!--针对oracle数据库的方言,特定的关系数据库生成优化的SQL-->
<prop key="hibernate.dialect">${jdbc.dialect}</prop>
<!-- 是否在控制台打印sql语句    -->
<prop key="hibernate.show_sql">${jdbc.showSql}</prop>
<!-- 输出格式化后的sql,更方便查看    -->
<prop key="hibernate.format_sql">${jdbc.formatSql}</prop>
<!-- 该参数用来允许使用outer join来载入此集合的数据。 -->
<prop key="hibernate.use_outer_join">${jdbc.useOuterJoin}</prop>
<!-- 允许查询缓存,个别查询仍然需要被设置为可缓存的.-->
<prop key="hibernate.cache.use_query_cache">${jdbc.useQueryCache}</prop>
<!--每次从数据库中取出的记录条数,oracle的话建议设为100,至少50 -->
<prop key="hibernate.default_batch_fetch_size">${jdbc.defaultBatchFetchSize}</prop>
<!--连接池的最大活动个数    -->
<prop key="hibernate.dbcp.maxActive">100</prop>
<!-- 当连接池中的连接已经被耗尽的时候,DBCP将怎样处理(0=失败,1=等待,2=增长)-->
<prop key="hibernate.dbcp.whenExhaustedAction">1</prop>
<!-- 最大等待时间 -->
<prop key="hibernate.dbcp.maxWait">1200</prop>
<!-- 没有人用连接的时候,最大闲置的连接个数     -->
<prop key="hibernate.dbcp.maxIdle">20</prop>
<!-- 以下是对prepared statement的处理,同上。  -->
<prop key="hibernate.dbcp.ps.maxActive">100</prop>
<prop key="hibernate.dbcp.ps.whenExhaustedAction">1</prop>
<prop key="hibernate.dbcp.ps.maxWait">1200</prop>
<prop key="hibernate.dbcp.ps.maxIdle">10</prop>
</props>
</property>
<!-- 自动扫描实体对象 指定的包结构中存放实体类 -->
<property name="packagesToScan">
<list>
<value>com.A.dict</value>
<value>com.A.entity</value>
</list>
</property>
</bean>


d、配置动态SessionFactory

<bean id="sessionFactory" class="com.util.db.DynamicSessionFactory">
<property name="defaultTargetSessionFactory" ref="oracleSessionFactory" />
<property name="targetSessionFactorys">
<map key-type="java.lang.String">
<entry value-ref="oracleSessionFactory" key="oracle"/>
<entry value-ref="sqlServerSessionFactory" key="sqlserver"/>
</map>
</property>
</bean>


e、配置改写后的事务管理器

<bean id="transactionManager" class="com.util.db.DynamicTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>


  6、截止到目前为止,已经可以通过DBContextHolder.setDataSourceType(DataSource别名) 和 DBContextHolder.setSessionFactoryType(SessionFactory别名)来手动切换数据源和SessionFactory了。接下来,将通过AOP拦截DAO层,在DAO调用方法之前,切换到相应的数据库。

a、配置Spring

<bean id="dataSourceInterceptor" class="com.util.db.DBInterceptor" />

<!--加了下面这句以后,使用CGLIB实现AOP,可以不用接口类直接用实现类-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 定义切面,在 * com.gcucwc.service.*(..) 中执行有关的hibernate session的事务操作 -->
<aop:config>
<aop:pointcut id="serviceOperation" expression="execution(* com.*.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />

<aop:pointcut id="dsA" expression="execution(* com.A.dao.*.*(..))" />
<aop:pointcut id="dsB" expression="execution(* com.B.dao.*.*(..))" />
<aop:pointcut id="dsC" expression="execution(* com.C.dao.*.*(..))" />
<aop:pointcut id="dsD" expression="execution(* com.D.dao.*.*(..))" />

<aop:aspect id="dataSourceAspect" ref="dataSourceInterceptor">
<aop:before method="setOld" pointcut-ref="dsOld"/>
<aop:before method="setNew" pointcut-ref="dsNew"/>
<aop:before method="setSun" pointcut-ref="dsSun"/>
<aop:before method="setSmart" pointcut-ref="dsSmart"/>
</aop:aspect>
</aop:config>

<!-- 该 BeanPostProcessor 将自动对标注 @Autowired 的 Bean 进行注入 -->
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor"/>


b、新建拦截类

package com.util.db;

import org.aspectj.lang.JoinPoint;
import static com.util.db.DBContextHolder.*;

public class DBInterceptor {

public void setOld(JoinPoint jp) {
setDataSourceType(DATASOURCE_A);
setSessionFactoryType(SESSION_FACTORY_SQLSERVER);
}

public void setNew(JoinPoint jp) {
setDataSourceType(DATASOURCE_B);
setSessionFactoryType(SESSION_FACTORY_SQLSERVER);
}

public void setSun(JoinPoint jp) {
setDataSourceType(DATASOURCE_C);
setSessionFactoryType(SESSION_FACTORY_SQLSERVER);
}

public void setSmart(JoinPoint jp) {
setDataSourceType(DATASOURCE_D);
setSessionFactoryType(SESSION_FACTORY_ORACLE);
}
}


7、最后是DAO层的基础类的一些注意事项

public class BaseDAOImpl<T, ID extends Serializable> implements BaseDAO<T, ID> {

@Autowired
private DynamicSessionFactory sessionFactory; //注入的是DynamicSessionFactory

public DynamicSessionFactory getSessionFactory() {
return sessionFactory;
}

//需要注意的获取session方式
public Session getSession() {
return sessionFactory.getHibernateSessionFactory().getCurrentSession();
}

//示范方法
@Override
public void save(T t) {
this.getSession().save(t);
}
}


注意:如果Service层直接调用DAO层未重写的BaseDAOImpl的方法,并不会触发相应的AOP。例如UserDAO extends BaseDAOImpl,然后UserDAO未重写save方法,此时调用UserDAO.save(T)并不会自动切换数据源。

本文内容建立在前辈们的成果基础上,非完全本人原创,如有错漏烦请指正。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息