Spring配置动态数据源

Spring 配置动态数据源

Spring配置动态数据源,是在大型应用中对数据进行切换。一个使用场景就是,万一数据库的master实例挂了怎么办,如何动态的将从数据库替代上去呢? 保证数据库正常的使用呢? 那么Spring动态数据源的配置,就可以很好的解决这个问题,可以将数据源动态切换,将数据源切换到备用的数据库中。

配置多数据源有多中方案:

方案一: 配置多个数据源, 多个数据源分别都有一个sqlSessionFactory, 在使用时分表注入,往哪个库写就就使用哪个sqlSessionFactory
缺点: 修改麻烦,不符合开闭原则

方案二: 配置多个数据源,多个数据源都是使用同一个sqlSessionFactory
优点: 易于维护

Spring 2.x 以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且使用它来封装数据源选择逻辑,这样就可以有效的将数据源选择逻辑从Client中分离出来,Client提供选择所需的上下文,由虚拟的DataSource根据Client提供上下文来实现。由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!-- 配置数据源 value 为数据库配置文件内的参数-->
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- 使用数据源一的连接配置, 连接名称自己命名,这里的命名规则为了使读者明白,故在后面添加了数字-->
<property name="driverClassName" value="${jdbc.driverClassName1}"/>
<property name="url" value="${jdbc.url1}"/>
<property name="username" value="${jdbc.username1}"/>
<property name="password" value="${jdbc.password1}"/>
<property name="maxActive" value="${maxActive1}"/>
<property name="maxWait" value="${maxWait1}"/>
<property name="maxIdle" value="${maxIdle1}"/>
</bean>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
<!-- 使用数据源二的连接配置-->
<property name="driverClassName" value="${jdbc.driverClassName2}"/>
<property name="url" value="${jdbc.url2}"/>
<property name="username" value="${jdbc.username2}"/>
<property name="password" value="${jdbc.password2}"/>
<property name="maxActive" value="${maxActive2}"/>
<property name="maxWait" value="${maxWait2}"/>
<property name="maxIdle" value="${maxIdle2}"/>
</bean>
<!--
配置一个动态的DynamicDataSource类 该类自己定义, 继承了AbstractRoutingDataSource
-->
<bean id="dataSource" class="com.jd.jr.dao.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSource1" key="dataSource1"></entry>
<entry value-ref="dataSource2" key="dataSource2"></entry>
</map>
</property>
<!-- 默认的是 dataSource1的数据源 -->
<property name="defaultTargetDataSource" ref="dataSource1"></property>
</bean>
<!--创建myBatis itessession -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--数据源-->
<property name="dataSource" ref="dataSource1"/>
<!--映射文件-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<!--配置数据表对应的Java实体类-->
<property name="typeAliasesPackage" value="com.jd.jr.bean"/>
<!--mybaits配置文件-->
<property name="configLocation" value="classpath:mybaits-config.xml"/>
</bean>

创建一个获取和设置上下文环境的类DataSourceContextHolder,主要负责改变上下文数据源的名称

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class DataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
private static final ThreadLocal<String> tableIndexHolder= new ThreadLocal<String>();
public static String getTableIndex(){
return (String)tableIndexHolder.get();
}
public static void clearTableIndex(){
tableIndexHolder.remove();
}
public static void setTableIndex(String tableIndex){
tableIndexHolder.set(tableIndex);
}
public static void setDBType(String dbType) {
contextHolder.set(dbType);
}
public static String getDBType() {
return ((String) contextHolder.get());
}
public static void clearDBType() {
contextHolder.remove();
}
}

创建动态数据源类:
DynamicDataSource:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* Author: wangchangchun
* Date: 2017-11-15
* Description:
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDBType();
}
}

注意: 如果需要切换数据源, 那么最好在调用Service 前将数据库中数据进行切换

在Facade 层中进行切换数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Component
public class TransactionOrderFadeImpl implements TransactionOrderFade {
private Logger logger = LoggerFactory.getLogger(TransactionOrderFadeImpl.class);
//注入自己的服务层
@Resource
private TransactionOrderService transactionOrderService;
public BaseResponseVo ceateOrder(PayOrderCreateRequestVo request) {
//创建一个 ceateOrder
BaseResponseVo baseResponseVo = null;
if (request==null || request.getPayOrderListVo() ==null || request.getPayOrderListVo().size() == 0){
throw new NoDataException("0001", "传入的参数是没严格的初始化操作,传入的常数为空");
}
// 将数据库切换
//logger.info("将数据库的数据源进行切换");
/**
* 对数据库进行切换 设置数据源 切换数据源最好在调用哪个Service 层中的具体的
* 业务操作之前进行切换
*/
DataSourceContextHolder.setDBType("dataSource2");
//......
}
//......
}

坚持原创技术分享,您的支持将鼓励我继续创作!