application.xml 配置如下
<!-- 使用 DruidDataSource 数据源 -->
<bean id="primaryDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
</bean>
<!-- 使用 数据源 1-->
<bean id="logDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
</bean>
<!-- 配置动态加载数据源 -->
<bean id="druidDynamicDataSource" class="com.iss.sso.utils.DynamicDataSource">
<property name="defaultTargetDataSource" ref="primaryDataSource" />
<property name="targetDataSources">
<map>
<entry key="primaryDataSource" value-ref="primaryDataSource"/>
<entry key="scheduleDataSource" value-ref="logDataSource"/>
</map>
</property>
</bean>
<!--3.配置SqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--往下才是mybatis和spring真正整合的配置-->
<!--注入数据库连接池-->
<property name="dataSource" ref="druidDynamicDataSource"/>
<!--
配置mybatis全局配置文件:mybatis-config.xml
指定Mybatis的配置文件位置。如果指定了该属性,
那么会以该配置文件的内容作为配置信息构建对应的SqlSessionFactoryBuilder,
但是后续属性指定的内容会覆盖该配置文件里面指定的对应内容
-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!--
扫描entity包,使用别名,多个用;隔开
一般对应实体类所在的包,这个时候会自动取对应包中不包括包名的简单类名作为包括包名的别名。
多个package之间可以用逗号或者分号等来进行分隔。(value的值一定要是包的全名)
-->
<property name="typeAliasesPackage" value="com.sso.**.domain"/>
<!--
扫描sql配置文件:mapper需要的xml文件
Mapper文件存放的位置,当Mapper文件跟对应的Mapper接口处于同一位置的时候可以不用指定该属性的值
-->
<property name="mapperLocations" value="classpath:/mapper/*.xml"/>
</bean>
----- 动态数据源
<bean id="dynamicDataSourceAspect" class="com.sso.utils.DynamicDataSourceAspect">
</bean>
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引入数据源-->
<property name="dataSource" ref="druidDynamicDataSource"/>
</bean>
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!--定义事务增强,并制定事务管理器 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!--设置传播行为-->
<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="del*" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="select*" propagation="SUPPORTS" isolation="DEFAULT" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="myPointcut" expression="execution(* com.sso.*.dao.*.*(..))"/>
<aop:advisor advice-ref="dynamicDataSourceAspect" pointcut-ref="myPointcut" order="1"/>
<!--把事务控制在Service层-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" order="2"/>
</aop:config>
动态数据源 核心类
/**
* 动态数据源加载
*
* @author
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
/**
* 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰
*/
private static final ThreadLocal<String> key = new ThreadLocal<String>();
@Override
protected Object determineCurrentLookupKey() {
logger.debug("===当前数据源: {}===", key.get());
return key.get();
}
/**
* 设置数据源
*
* @param dataSource 数据源名称
*/
public static void setDataSource(String dataSource) {
key.set(dataSource);
}
/**
* 获取数据源
*
* @return
*/
public static String getDatasource() {
return key.get();
}
/**
* 清除数据源
*/
public static void clearDataSource() {
key.remove();
}
}
spring aop 植入
@Aspect
@Order(-10)
@Component
public class DynamicDataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice {
Logger log = LoggerFactory.getLogger("切换数据源");
/**
* 目标方法正常完成后被织入,关闭数据源
*
* @param o
* @param method
* @param objects
* @param o1
* @throws Throwable
*/
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) {
//这里做一个判断,有使用DataSourceAnnotation注解时才关闭数据源,有一个主要的数据源,就没有必要每次都去关闭
if (method.isAnnotationPresent(DataSourceAnnotation.class)) {
DynamicDataSource.clearDataSource();
log.debug("数据源已关闭");
}
}
/**
* 拦截目标方法,获取由@DataSourceAnnotation指定的数据源标识,设置到线程存储中以便切换数据源
*/
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
if (method.isAnnotationPresent(DataSourceAnnotation.class)) {
String clazzName = method.getDeclaringClass().getName();
String methodName = method.getName();
log.info("{}.{} 准备切换数据源", clazzName, methodName, DynamicDataSource.getDatasource());
DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);
DynamicDataSource.setDataSource(dataSourceAnnotation.value());
log.info("{}.{} 数据源切换为:{}", clazzName, methodName, DynamicDataSource.getDatasource());
} else {
DynamicDataSource.setDataSource(DataSourceAnnotation.PRIMARY);
}
}
}