透过识别方法注解,自动切换主备数据源DynamicDataSource
org.springframework.jdbc.datasource.lookup,数据库主备配置
mysql.properties:
crm.jdbc.driverClassName=com.mysql.jdbc.Driver
crm.jdbc.url=jdbc:mysql://10.100.163.77:3306/financial_sales_prod?useUnicode=true&characterEncoding=utf-8
crm.jdbc.username=telsale/-pl
crm.jdbc.password=0OKM
crm.dbcp.initialSize=5
crm.dbcp.minIdle=5
crm.dbcp.maxActive=20
crm.dbcp.maxWait=10000
crm.dbcp.timeBetweenEvictionRunsMillis=60000
crm.dbcp.minEvictableIdleTimeMillis=300000
crm.dbcp.testWhileIdle=true
crm.dbcp.testOnBorrow=true
crm.dbcp.testOnReturn=false
crm.dbcp.poolPreparedStatements=true
crm.dbcp.maxPoolPreparedStatementPerConnectionSize=20
bigdata.jdbc.driverClassName=com.mysql.jdbc.Driver
bigdata.jdbc.url=jdbc:mysql://10.100.163.78:3306/financial_sales_prod?useUnicode=true&characterEncoding=utf-8
bigdata.jdbc.username=telsale/-pl
bigdata.jdbc.password=0OKM
bigdata.dbcp.initialSize=5
bigdata.dbcp.minIdle=5
bigdata.dbcp.maxActive=20
bigdata.dbcp.maxWait=10000
bigdata.dbcp.timeBetweenEvictionRunsMillis=60000
bigdata.dbcp.minEvictableIdleTimeMillis=300000
bigdata.dbcp.testWhileIdle=true
bigdata.dbcp.testOnBorrow=true
bigdata.dbcp.testOnReturn=false
bigdata.dbcp.poolPreparedStatements=true
bigdata.dbcp.maxPoolPreparedStatementPerConnectionSize=20
applicationContext-dao.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress ALL -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<bean id="crm_dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${crm.jdbc.driverClassName}"/>
<property name="url" value="${crm.jdbc.url}"/>
<property name="username" value="${crm.jdbc.username}"/>
<property name="password" value="${crm.jdbc.password}"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${crm.dbcp.initialSize}"/>
<property name="minIdle" value="${crm.dbcp.minIdle}"/>
<property name="maxActive" value="${crm.dbcp.maxActive}"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${crm.dbcp.maxWait}"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${crm.dbcp.timeBetweenEvictionRunsMillis}"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${crm.dbcp.minEvictableIdleTimeMillis}"/>
<property name="testWhileIdle" value="${crm.dbcp.testWhileIdle}"/>
<!-- 防止取到的连接不可用, 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能-->
<property name="testOnBorrow" value="${crm.dbcp.testOnBorrow}"/>
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="${crm.dbcp.testOnReturn}"/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。5.5及以上版本有PSCache,建议开启-->
<property name="poolPreparedStatements" value="${crm.dbcp.poolPreparedStatements}"/>
<property name="maxPoolPreparedStatementPerConnectionSize"
value="${crm.dbcp.maxPoolPreparedStatementPerConnectionSize}"/>
<!-- 这里配置提交方式,默认就是TRUE,可以不用配置 -->
<property name="defaultAutoCommit" value="true"/>
<!-- 验证连接有效与否的SQL,不同的数据库配置不同 -->
<property name="validationQuery" value="select 1 "/>
<!-- 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall-->
<property name="filters" value="stat"/>
<!-- <property name="proxyFilters">
<list>
<ref bean="logFilter"/>
</list>
</property> -->
</bean>
<bean id="bigdata_dataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<property name="driverClassName" value="${bigdata.jdbc.driverClassName}"/>
<property name="url" value="${bigdata.jdbc.url}"/>
<property name="username" value="${bigdata.jdbc.username}"/>
<property name="password" value="${bigdata.jdbc.password}"/>
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${bigdata.dbcp.initialSize}"/>
<property name="minIdle" value="${bigdata.dbcp.minIdle}"/>
<property name="maxActive" value="${bigdata.dbcp.maxActive}"/>
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${bigdata.dbcp.maxWait}"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${bigdata.dbcp.timeBetweenEvictionRunsMillis}"/>
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${bigdata.dbcp.minEvictableIdleTimeMillis}"/>
<property name="testWhileIdle" value="${bigdata.dbcp.testWhileIdle}"/>
<!-- 防止取到的连接不可用, 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能-->
<property name="testOnBorrow" value="${bigdata.dbcp.testOnBorrow}"/>
<!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
<property name="testOnReturn" value="${bigdata.dbcp.testOnReturn}"/>
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。5.5及以上版本有PSCache,建议开启-->
<property name="poolPreparedStatements" value="${bigdata.dbcp.poolPreparedStatements}"/>
<property name="maxPoolPreparedStatementPerConnectionSize"
value="${bigdata.dbcp.maxPoolPreparedStatementPerConnectionSize}"/>
<!-- 这里配置提交方式,默认就是TRUE,可以不用配置 -->
<property name="defaultAutoCommit" value="true"/>
<!-- 验证连接有效与否的SQL,不同的数据库配置不同 -->
<property name="validationQuery" value="select 1 "/>
<!-- 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall-->
<property name="filters" value="stat"/>
<property name="proxyFilters">
<list>
<ref bean="logFilter"/>
</list>
</property>
</bean>
<bean id="logFilter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter">
<property name="statementExecutableSqlLogEnable" value="false"/>
<property name="resultSetLogEnabled" value="false"/>
</bean>
<bean id="dynamicDataSource" class="com.houbank.incoming.dao.datasource.DynamicDataSource" >
<property name="targetDataSources" >
<map>
<entry key="crm_dataSource" value-ref="crm_dataSource" /> //两个数据源
<entry key="bigdata_dataSource" value-ref="bigdata_dataSource" />
</map>
</property>
<property name="defaultTargetDataSource" ref="crm_dataSource" /> //主库
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource"/>
<property name="mapperLocations">
<array>
<value>classpath*:mapper/*.xml</value>
<value>classpath*:WorkProcessorMapper/*.xml</value>
</array>
</property>
<property name="configLocation" value="classpath:/META-INF/spring/mybatis-configuration.xml"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.houbank.incoming.dao.mapper"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
<!--动态数据源切面配置-->
<bean id="dataSourceAspect" class="com.houbank.incoming.dao.datasource.DynamicDataSourceAspect" /> //切面配置
<!-- 事务配置开始 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource"/>
</bean>
<tx:annotation-driven/>
<!--对于方法的细粒度事务配置,符合条件的方法冠以相应的事务-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="delete*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="remove*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="insert*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="add*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="save*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="submit*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="newTrans*" propagation="REQUIRES_NEW" read-only="false"
rollback-for="java.lang.Exception"/>
<tx:method name="modify*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="query*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!--把事务控制在Service层 -->
<aop:config>
<!--这里配置规则,满足以下规则的将触发事务,第一个*表示所有返回类型,第二个表示service包下的所有class,第三个表示所有方法-->
<aop:pointcut id="servicePointCut"
expression="execution(public * com.houbank.incoming.service.*.*(..))"/>
<aop:advisor pointcut-ref="servicePointCut" advice-ref="txAdvice"/>
<!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级最高执行 -->
<aop:advisor advice-ref="dataSourceAspect" pointcut-ref="servicePointCut" order="1" />
</aop:config>
<!-- 事务配置结束 -->
</beans>
java:
自定义数据源:
package com.houbank.incoming.dao.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
/**
* Created by xiaogaoxu on 2016/8/25.
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DatasourceHold.getDBType();
}
@Override
public DataSource determineTargetDataSource() {
return super.determineTargetDataSource();
}
}
自定义数据源切面:
package com.houbank.incoming.dao.datasource;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;
/**
* Created by xiaogaoxu on 2016/8/25.
*/
public class DynamicDataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
DatasourceHold.clearDBType();
}
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
if (method.isAnnotationPresent(DataSource.class)) { //这里通过方法的注解区分哪些方式使用哪种数据库,大数据查询的专门使用查询的库
DataSource datasource = method.getAnnotation(DataSource.class);
DatasourceHold.setDBType(datasource.name());
} else {
DatasourceHold.clearDBType();
}
}
}
package com.houbank.incoming.dao.datasource;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
/**
* CRM系统的主数据源 系统默认
*/
String CRM_DATASOURCE = "crm_dataSource";
/**
* 大数据的数据源 只读同步数据
*/
String BIGDATA_DATASOURCE = "bigdata_dataSource";
String name();
}
mybatis采用的是多数据源配置
crm_dataSource CRM系统的主数据源 bigdata_dataSource 大数据的数据源 只读同步数据 示例: @DataSource(name = DataSource.BIGDATA_DATASOURCE) public List<TagFilterRel> selectByCondition(TagFilterRel rel) { return tagFilterRelMapper.selectByCondition(rel); } mybatis数据源及事务配置见applicationContext-dao.xml