Spring配置动态数据源,是在大型应用中对数据进行切换。一个使用场景就是,万一数据库的master实例挂了怎么办,如何动态的将从数据库替代上去呢? 保证数据库正常的使用呢? 那么Spring动态数据源的配置,就可以很好的解决这个问题,可以将数据源动态切换,将数据源切换到备用的数据库中。
配置多数据源有多中方案:
方案一: 配置多个数据源, 多个数据源分别都有一个sqlSessionFactory, 在使用时分表注入,往哪个库写就就使用哪个sqlSessionFactory
缺点: 修改麻烦,不符合开闭原则
方案二: 配置多个数据源,多个数据源都是使用同一个sqlSessionFactory
优点: 易于维护
Spring 2.x 以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且使用它来封装数据源选择逻辑,这样就可以有效的将数据源选择逻辑从Client中分离出来,Client提供选择所需的上下文,由虚拟的DataSource根据Client提供上下文来实现。由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。
<!-- 配置数据源 value 为数据库配置文件内的参数--> <beanid="dataSource1"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close"> <!-- 使用数据源一的连接配置, 连接名称自己命名,这里的命名规则为了使读者明白,故在后面添加了数字--> <propertyname="driverClassName"value="${jdbc.driverClassName1}"/> <propertyname="url"value="${jdbc.url1}"/> <propertyname="username"value="${jdbc.username1}"/> <propertyname="password"value="${jdbc.password1}"/> <propertyname="maxActive"value="${maxActive1}"/> <propertyname="maxWait"value="${maxWait1}"/> <propertyname="maxIdle"value="${maxIdle1}"/> </bean> <beanid="dataSource2"class="org.apache.commons.dbcp.BasicDataSource"> <!-- 使用数据源二的连接配置--> <propertyname="driverClassName"value="${jdbc.driverClassName2}"/> <propertyname="url"value="${jdbc.url2}"/> <propertyname="username"value="${jdbc.username2}"/> <propertyname="password"value="${jdbc.password2}"/> <propertyname="maxActive"value="${maxActive2}"/> <propertyname="maxWait"value="${maxWait2}"/> <propertyname="maxIdle"value="${maxIdle2}"/> </bean> <!-- 配置一个动态的DynamicDataSource类 该类自己定义, 继承了AbstractRoutingDataSource --> <beanid="dataSource"class="com.jd.jr.dao.DynamicDataSource"> <propertyname="targetDataSources"> <mapkey-type="java.lang.String"> <entryvalue-ref="dataSource1"key="dataSource1"></entry> <entryvalue-ref="dataSource2"key="dataSource2"></entry> </map> </property> <!-- 默认的是 dataSource1的数据源 --> <propertyname="defaultTargetDataSource"ref="dataSource1"></property> </bean> <!--创建myBatis itessession --> <beanid="sqlSessionFactory"class="org.mybatis.spring.SqlSessionFactoryBean"> <!--数据源--> <propertyname="dataSource"ref="dataSource1"/> <!--映射文件--> <propertyname="mapperLocations"value="classpath:mapper/*.xml"/> <!--配置数据表对应的Java实体类--> <propertyname="typeAliasesPackage"value="com.jd.jr.bean"/> <!--mybaits配置文件--> <propertyname="configLocation"value="classpath:mybaits-config.xml"/> </bean>
创建一个获取和设置上下文环境的类DataSourceContextHolder,主要负责改变上下文数据源的名称
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:
/** * Author: wangchangchun * Date: 2017-11-15 * Description: */ public class DynamicDataSourceextends AbstractRoutingDataSource{ @Override protected Object determineCurrentLookupKey(){ return DataSourceContextHolder.getDBType(); } }
注意: 如果需要切换数据源, 那么最好在调用Service 前将数据库中数据进行切换
在Facade 层中进行切换数据源
@Component public class TransactionOrderFadeImplimplements 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"); //...... } //...... }