spring多数据源使用详解
数据库读写分离必然会引入多数据源,那么spring项目如何实现多数据源呢?
思路:spring在每次操作数据库的时候都会通过AbstractRoutingDataSource类中的determineTargetDataSource()方法获取当前数据源,我们可以通过AOP技术,在不同的切面,切入不同的数据源名称,使得spring获取的时候拿到的是不同的数据源。当然我们也可以不实用AOP技术,而是在数据库操作前手动改变将要实用的数据源名称,从而使得spring获取的时候拿到我们想要的数据源。
determineCurrentLookupKey()方法是抽象的,我们是可以实现这个类,重写此方法,然后按照思路中的想法来实现多数据源的自由切换
下面我们具体来实现:
第一步:首先在spring的配置文件中进行多数据源配置
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> //这里我只写关键配置点 <!-- 第一个基于druid的datasource --> <bean id="dataSource1" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> //连接信息省略 </bean> <!-- 第二个基于druid的datasource --> <bean id="dataSource2" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close"> //连接信息省略 </bean> <!-- 数据源集合 --> <bean id="dataSource" class="winclpt.spring.DynamicDataSource"> <property name="targetDataSources"> <map key-type="winclpt.spring.DataSourceType"> <entry key="dataSource1" value-ref="dataSource1" /> <entry key="dataSource2" value-ref="dataSource2" /> </map> </property> <property name="defaultTargetDataSource" ref="dataSource1" /> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="entityInterceptor" ref="hiberAspect" /> <property name="hibernateProperties"> <props> <!--<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> --> <prop key="hibernate.dialect">${hibernate.dialect}</prop> <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop> </props> </property> <!-- 注解方式配置 --> <property name="packagesToScan"> <list> <value></value> </list> </property> </bean> <!-- 配置事物管理器,在*ServiceImpl里写@Transactional就可以启用事物管理 --> <bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <bean id="dataSourceInterceptor" class="winclpt.spring.DSAscept"/> <aop:config> <aop:aspect ref="dataSourceInterceptor"> <aop:pointcut id="test1" expression="execution(* winclpt.spring.*.*(..))" /> <aop:pointcut id="test2" expression="execution(* winclpt.service.*.*(..))" /> <aop:before pointcut-ref="test1" method="setdataSource1" />//在test1切面设置数据源为dataSource1 <aop:before pointcut-ref="test2" method="setdataSource2" />//在test1切面设置数据源为dataSource1
</aop:aspect> </aop:config> </beans>
DynamicDataSource.java
public class DynamicDataSource extends AbstractRoutingDataSource { /* * 该方法必须要重写 方法是为了根据数据库标示符取得当前的数据库 */ protected Object determineCurrentLookupKey() { DataSourceType dataSourceType= DataSourceContextHolder.getDataSourceType(); return dataSourceType; } public void setDataSourceLookup(DataSourceLookup dataSourceLookup) { super.setDataSourceLookup(dataSourceLookup); } public void setDefaultTargetDataSource(Object defaultTargetDataSource) { super.setDefaultTargetDataSource(defaultTargetDataSource); } public void setTargetDataSources(Map targetDataSources) { super.setTargetDataSources(targetDataSources); } }
DataSourceType.java
public enum DataSourceType { dataSource1,dataSource2//所有spring xml中配置的数据源名称都应该在这里存在 }
DataSourceContextHolder.java
/** *类名:DataSourceContextHolder.java *功能:获得和设置上下文环境的类,主要负责改变上下文数据源的名称 */ public class DataSourceContextHolder { private static final ThreadLocal contextHolder=new ThreadLocal(); public static void setDataSourceType(DataSourceType dataSourceType){ contextHolder.set(dataSourceType); } public static DataSourceType getDataSourceType(){ return (DataSourceType) contextHolder.get(); } public static void clearDataSourceType(){ contextHolder.remove(); } }
DSAscept.java
public class DSAscept{ public viod setdataSource1(){ DataSourceContextHolder.setDataSourceType(DataSourceType.dataSource1); } public viod setdataSource2(){ DataSourceContextHolder.setDataSourceType(DataSourceType.dataSource2); } }
通过上面的操作,我们就可以通过AOP技术轻松的实现不同方法不同数据源,切换自由
当然我们也可以不实用AOP切面技术,可以在入库操作前手动调用如下方法改变当前ThreadLocal中的数据源名称,从而实现多数据源切换
public void save(int id){ DataSourceContextHolder.setDataSourceType(DataSourceType.winclpt); //数据库操作 }