其实就是通过AOP实现:
思路:
请求---目标方法前置增强---选择数据库类型(读写数据库类型选择)
1.请求
2.前置增强:根据根据方法名称类型,设置不同key
3.实现数据库的选择:根据(2)设置的不同的key,选择不同类型的数据库
4.配置AOP声明式事务
1.略
2.前置增强:根据根据方法名称类型,设置不同key
public class DataSourceMethodBeforeAdvice implements MethodBeforeAdvice {
private MultiDataSource multiDataSource;
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
//1.判断方法名类型
String methodName = method.getName();
//2.设置该次请求要选择的DataSource类型的key
if (StringUtils.startsWith(methodName,"add")
|| StringUtils.startsWith(methodName,"insert")
|| StringUtils.startsWith(methodName,"delete")) {
multiDataSource.setDataSourceKey("master");
} else {
multiDataSource.setDataSourceKey("slave1");
}
}
public void setMultiDataSource(MultiDataSource multiDataSource) {
this.multiDataSource = multiDataSource;
}
}
3.实现数据库的选择:根据(2)设置的不同的key,选择不同类型的数据库
public class MultiDataSource extends AbstractRoutingDataSource {
private String dataSourceKey;
@Override
protected Object determineCurrentLookupKey() {
return dataSourceKey;
}
public void setDataSourceKey(String dataSourceKey) {
System.out.println("=============DataSourceKey:"+dataSourceKey);
this.dataSourceKey = dataSourceKey;
}
}
4.配置AOP声明式事务
<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean
below) 事务传播特性配置 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<tx:method name="insert*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<tx:method name="modify*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.Exception"/>
<!-- 查询方法 -->
<tx:method name="query*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务管理器 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean class="cn.test.aop.DataSourceMethodBeforeAdvice" id="dataSourceMethodBeforeAdvice">
<property name="multiDataSource" ref="dataSource"/>
</bean>
<!-- 声明式事务AOP配置 -->
<aop:config>
<aop:pointcut expression="execution(* cn.test.service.impl.*.*(..))" id="tranpointcut"/>
<aop:advisor advice-ref="dataSourceMethodBeforeAdvice" pointcut-ref="tranpointcut"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="tranpointcut"/>
</aop:config>
<bean class="cn.test.datasource.MultiDataSource" id="dataSource">
<!-- 设置默认数据源 -->
<property name="defaultTargetDataSource" ref="masterDataSource1"/>
<!-- 设置所有的数据源与其对应的key -->
<property name="targetDataSources">
<map>
<entry key="master" value-ref="masterDataSource1"/>
<entry key="slave1" value-ref="slaveDataSource1"/>
<entry key="slave2" value-ref="slaveDataSource2"/>
</map>
</property>
</bean>
<!-- 数据库基本信息配置 -->
<bean id="masterDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<bean id="slaveDataSource1" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
<bean id="slaveDataSource2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.pwd}"/>
</bean>
需要注意的是determineTargetDataSource()是在需要的时候才会被调用,即如上配置,service有事物控制的,则从service开始调用,没有事物控制的则从mapper开始调用。
有一些细节可以优化,这里只做简单使用举例说明。