近期项目中需要对接其他系统,涉及到多个数据源的配置,在这里记录一下,方便以后自己查阅。
使用的数据库是sqlserver,步骤如下:
一、修改db.properties中的配置信息
jdbc.url=jdbc:sqlserver://localhost:1433;DatabaseName=test1
jdbc.username=3oJ82PFqtGI=
jdbc.password=yq3r3NucCgywOy7FXoydpg==
jdbc.urlsecend=jdbc:sqlserver://localhost:1433;DatabaseName=test2
jdbc.usernamesecend=3oJ82PFqtGI=
jdbc.passwordsecend=yq3r3NucCgywOy7FXoydpg==
我这里的数据库连接信息做了加密,不加密也没有影响。
二、创建@DataSourceAnnotation 注解使用于aop进行数据源的切换
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceAnnotation {
String value();
String primary = "primary";
String secend= "secend";
}
三、创建动态数据源DynamicDataSource继承AbstractRoutingDataSource
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 数据源标识,保存在线程变量中,避免多线程操作数据源时互相干扰
*/
private static final ThreadLocal<String> key = new ThreadLocal<String>();
@Override
protected Object determineCurrentLookupKey() {
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();
}
}
四、创建一个aop切面作用于目标方法上
@Component
public class DynamicDataSourceAspect implements MethodBeforeAdvice, AfterReturningAdvice {
Logger log = LoggerFactory.getLogger("切换数据源");
@Override
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
//这里做一个判断,有使用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)) {
DataSourceAnnotation dataSourceAnnotation = method.getAnnotation(DataSourceAnnotation.class);
DynamicDataSource.setDataSource(dataSourceAnnotation.value());
log.debug("数据源切换为:" + DynamicDataSource.getDatasource());
}
}
}
五、配置动态数据源,将数据源交由sqlsessionfactory去处理
<bean id="dataSourcePrimary" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<...其他配置,不一一列举>
</bean>
<bean id="dataSourceSecend" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
destroy-method="close">
<property name="url" value="${jdbc.urlsecend}"></property>
<property name="username" value="${jdbc.usernamesecend}"></property>
<property name="password" value="${jdbc.passwordsecend}"></property>
<property name="driverClassName" value="${jdbc.driver}"></property>
<...其他配置,不一一列举>
</bean>
<!-- 动态数据源配置 -->
<bean id="dataSource" class="com.test.config.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="primary" value-ref="dataSourcePrimary"></entry>
<entry key="secend" value-ref="dataSourceSecend"></entry>
</map>
</property>
<!-- 配置默认数据源 -->
<property name="defaultTargetDataSource" ref="dataSourcePrimary"></property>
</bean>
六、配置aop
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--引入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="dynamicDataSourceAspect" class="com.test.config.DynamicDataSourceAspect">
</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="delete*" propagation="REQUIRED" isolation="DEFAULT"/>
<tx:method name="update*" 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>
<!--设置切面 com.test.service.*.impl.*()-->
<aop:config proxy-target-class="true">
<aop:pointcut id="myPointcut" expression="execution(* com.test.service.*.impl.*.*(..))"/>
<aop:advisor advice-ref="dynamicDataSourceAspect" pointcut-ref="myPointcut" order="1"/>
<!--把事务控制在Service层-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut" order="2"/>
</aop:config>
这里需要注意的是order值越小,优先级越高,所以切换数据源order的值要比事务切面的值小,否则会出现数据源切换失败!
七、在需要切换为非默认数据的方法上加@DataSourceAnnotation(DataSourceAnnotation.secend)就可以完成数据源的切换了。
@DataSourceAnnotation(DataSourceAnnotation.secend)
@Override
public List<User> listUser() {
List<User> listUser = userMapper.selectListUser();
return listUser;
}