【sping揭秘】21、Spring动态数据源的切换
对于多个数据源的时候,我们如何切换不同的数据源进行数据库的操作呢?
当然我们可以直接定义2个DataSource,然后在每次获取connection的时候,从不同的DataSource中获取connection,类似如下

这种情况可以是2个数据库存放的数据性质是不同的,DataSource1存放1种数据,DataSource2存放另一种数据,每个数据库承担不同的数据访问请求,这2个是完全相互独立不相干的
这种就比较简单,那就是直接定义不同的jdbctemplate,设置不同的DataSource就可以了,但是要注意代码编写的时候入库操作
还有一种就是数据性质是一样的,不同的数据源失去了独立自主的地位,这样所有的数据访问我们需要通过“盟主”进行。
这里我们可以借助spring的AbstractRoutingDataSource进行分发

后面几个数据库之间的数据共享,我们可以进行数据库数据的主从复制
定义数据源
package cn.cutter.start.database; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component; /**
* 自定义DataSource
* @author xiaof
*
*/
@Component
public class LiferayDataSource1 implements FactoryBean<DataSource> { @Override
public DataSource getObject() throws Exception {
BasicDataSource dataSource = new BasicDataSource(); //设置相应的参数
//1、数据库驱动类
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
//2、url,用户名,密码
dataSource.setUrl("jdbc:mysql://localhost:3306/liferay?characterEncoding=utf-8");
dataSource.setUsername("liferay"); dataSource.setPassword("xiaofeng2017");
//3、初始化连接大小
dataSource.setInitialSize(1);
//4、连接池最大数据量
dataSource.setMaxTotal(500);
//5、连接池最大小空闲
dataSource.setMinIdle(1);
dataSource.setMaxIdle(20);
//6、最大等待时间 单位毫秒
dataSource.setMaxWaitMillis(20 * 1000);
//7、指明连接是否被空闲连接回收器(如果有)进行检验
dataSource.setPoolPreparedStatements(true);
//8、运行一次空闲连接回收器的时间间隔(60秒)
dataSource.setTimeBetweenEvictionRunsMillis(60 * 1000);
//9、验证时使用的SQL语句
dataSource.setValidationQuery("SELECT 1 FROM DUAL");
//10、借出连接时不要测试,否则很影响性能
//11、申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
dataSource.setTestWhileIdle(false); return dataSource;
} @Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return DataSource.class;
} }
package cn.cutter.start.database; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.stereotype.Component; @Component
public class LiferayDataSource2 implements FactoryBean<DataSource> { @Override
public DataSource getObject() throws Exception {
BasicDataSource dataSource = new BasicDataSource(); //设置相应的参数
//1、数据库驱动类
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
//2、url,用户名,密码
dataSource.setUrl("jdbc:mysql://localhost:3306/test?characterEncoding=utf-8");
dataSource.setUsername("liferay"); dataSource.setPassword("xiaofeng2017");
//3、初始化连接大小
dataSource.setInitialSize(1);
//4、连接池最大数据量
dataSource.setMaxTotal(500);
//5、连接池最大小空闲
dataSource.setMinIdle(1);
dataSource.setMaxIdle(20);
//6、最大等待时间 单位毫秒
dataSource.setMaxWaitMillis(20 * 1000);
//7、指明连接是否被空闲连接回收器(如果有)进行检验
dataSource.setPoolPreparedStatements(true);
//8、运行一次空闲连接回收器的时间间隔(60秒)
dataSource.setTimeBetweenEvictionRunsMillis(60 * 1000);
//9、验证时使用的SQL语句
dataSource.setValidationQuery("SELECT 1 FROM DUAL");
//10、借出连接时不要测试,否则很影响性能
//11、申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效
dataSource.setTestWhileIdle(false); return dataSource;
} @Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return DataSource.class;
} }
package cn.cutter.start.database; import java.util.HashMap; import javax.annotation.PostConstruct;
import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component; /**
* 集合所有的数据源
* @author xiaof
*
*/
@Component
public class DataSources extends HashMap<Integer, DataSource> { @Autowired
@Qualifier("liferayDataSource1")
private DataSource liferayDataSource1; @Autowired
@Qualifier("liferayDataSource2")
private DataSource liferayDataSource2; @PostConstruct //创建对象之前进行注入
public void initDataSource() {
this.put(0, liferayDataSource1);
this.put(1, liferayDataSource2);
} }
动态路由类设置
package cn.cutter.start.database; import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import javax.annotation.Resource; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component; /**
* spring 中提供多数据源 高可用支持,合纵连横 多数据源
* @author xiaof
*
*/
@Component("multipleDataSource")
public class MultipleDataSource extends AbstractRoutingDataSource { private static final Log logger = LogFactory.getLog(MultipleDataSource.class); private Lock lock = new ReentrantLock();
private int counter = 0;
private int dataSourceNumber = 2; //对这个类的继承过来的对象进行分化
//1、注入defaultTargetDataSource
@Resource(name="liferayDataSource1")
public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
// 对于父类私有成员,就只能通过super访问
super.setDefaultTargetDataSource(defaultTargetDataSource);
} //2、注入所有数据源对象map targetDataSources
@Resource(name="dataSources")
@Override
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
super.setTargetDataSources(targetDataSources);
} @Override
protected Object determineCurrentLookupKey() { lock.lock(); try { ++counter;
//根据取余来进行取数对应的数据库源, 可以自定义自己的规则 来判断如何切换数据库
int lookupKey = counter % getDataSourceNumber();
logger.info("获取第:" + lookupKey + " 数据源");
return new Integer(lookupKey); } finally {
lock.unlock();
}
} public Lock getLock() {
return lock;
} public void setLock(Lock lock) {
this.lock = lock;
} public int getCounter() {
return counter;
} public void setCounter(int counter) {
this.counter = counter;
} public int getDataSourceNumber() {
return dataSourceNumber;
} public void setDataSourceNumber(int dataSourceNumber) {
this.dataSourceNumber = dataSourceNumber;
}
}
最后设置我们的动态jdbctemplate
package cn.cutter.start.database; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component; /**
* 多数据源切换,jdbctemplate使用
* @author xiaof
*
*/
@Component
public class MultipleJdbcTemplate implements FactoryBean<JdbcTemplate>{ @Autowired
@Qualifier("multipleDataSource")
private MultipleDataSource multipleDataSource; @Override
public JdbcTemplate getObject() throws Exception { JdbcTemplate jdbcTemplate = new JdbcTemplate(multipleDataSource);
return jdbcTemplate;
} @Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return JdbcTemplate.class;
} }
读取数据
package spring.vo;
public class DataVo {
private int num;
private String name = "cutter_point";
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
测试
@Test
public void testMultipleDataSource() {
ApplicationContext ctx = this.before(); //循环向数据库插入数据
String sql = "insert into multipleDataSourceTestTable values (?, ?)"; for(int i = 0; i < 10; ++i) {
//获取相应的数据连接 模拟项目中不同的业务场景获取jdbctemplate对象
JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("multipleJdbcTemplate");
DataVo dataVo = new DataVo();
dataVo.setNum(i);
jdbcTemplate.update(sql, new PreparedStatementSetter() { @Override
public void setValues(PreparedStatement ps) throws SQLException {
ps.setInt(1, dataVo.getNum());
ps.setString(2, dataVo.getName());
}
});
} // List<DataVo> datas = new ArrayList<DataVo>();
// for(int i = 0; i < 10; ++i) {
// DataVo dataVo = new DataVo();
// dataVo.setNum(i);
// datas.add(dataVo);
// }
//
// jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {
//
// @Override
// public void setValues(PreparedStatement ps, int i) throws SQLException {
// DataVo dataVo = datas.get(i);
// ps.setInt(1, dataVo.getNum());
// ps.setString(2, dataVo.getName());
// }
//
// @Override
// public int getBatchSize() {
// return datas.size();
// }
// }); }


【sping揭秘】21、Spring动态数据源的切换的更多相关文章
- Spring动态数据源的配置
Spring动态数据源 我们很多项目中业务都需要涉及到多个数据源,就是对不同的方法或者不同的包使用不同的数据源.最简单的做法就是直接在Java代码里面lookup需要的数据源,但是这种做法耦合性太高, ...
- Spring动态数据源实现读写分离
一.创建基于ThreadLocal的动态数据源容器,保证数据源的线程安全性 package com.bounter.mybatis.extension; /** * 基于ThreadLocal实现的动 ...
- spring动态数据源+事务
今天在尝试配置spring的动态数据源和事务管理的时候,遇到了几处配置上的问题,在此记录下: 1.使用了spring的aop思想,实现了动态数据源的切换. 2.spring的事务管理,是基于数据源的, ...
- Spring动态数据源-AbstractRoutingDataSource
在分库分表的情况下,在执行SQL时选择连接不同的数据源(库)的思路:配置多个数据源加到动态数据源对象中,根据实际的情况动态切换到相应的数据源中. 如存放订单信息的有10个库,每个库中有100张表,根据 ...
- Spring 动态创建并切换数据源
公司要求后端项目可以进行动态创建并切换数据源,看了网上很多例子大多数使用的都是Spring内置的AbstractRoutingDataSource进行的,使用此方法不是不行但是有诸多缺陷,比如切换时需 ...
- spring 动态数据源
1.动态数据源: 在一个项目中,有时候需要用到多个数据库,比如读写分离,数据库的分布式存储等等,这时我们要在项目中配置多个数据库. 2.原理: (1).spring 单数据源获取数据连接过程: ...
- @Transactional导致AbstractRoutingDataSource动态数据源无法切换的解决办法
上午花了大半天排查一个多数据源主从切换的问题,记录一下: 背景: 项目的数据库采用了读写分离多数据源,采用AOP进行拦截,利用ThreadLocal及AbstractRoutingDataSource ...
- Spring Boot数据访问之动态数据源切换之使用注解式AOP优化
在Spring Boot数据访问之多数据源配置及数据源动态切换 - 池塘里洗澡的鸭子 - 博客园 (cnblogs.com)中详述了如何配置多数据源及多数据源之间的动态切换.但是需要读数据库的地方,就 ...
- Spring Boot + Mybatis 实现动态数据源
动态数据源 在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动 ...
随机推荐
- 十四、ChainOfResponsibility 责任链模式
设计: 代码清单: Trouble: public class Trouble { private int number; public Trouble(int number){ this.numbe ...
- 阅读【现代网络技术 SDN/NFV/QOE 物联网和云计算】 第一章
本人打算阅读这本书来了解物联网和云计算的基础架构和设计原理.特作笔记如下: 作者: William Stallings 本书解决的主要问题: 由单一厂商例如IBM向企业或者个人提供IT产品和服务,包 ...
- vue 根据接口返回的状态码判断用户登录状态并跳转登录页,登录后回到上一个页面(http拦截器)
背景:后台接口返回code==501表示用户是未登录状态,需要登录才可访问: 通过http拦截做路由跳转 第一步:src目录下新建http.js文件,内容如下: import Axios from ' ...
- [Java学习]多线程
关于多进程与多线程 使用多进程的目的:提高CPU利用率. 使用多线程的目的:提高应用程序?利用率. 多线程与多进程区别:进程间内存独立:同一个进程的线程间共享"堆内存和方法区内存" ...
- Linux下Mysql安装(tar安装)
1.为数据库创建软件目录以及数据存放目录 #mysql软件目录 mkdir /software/ #mysql数据文件目录 mkdir /data/mysql 2.上传mysql-XXXXXX.tar ...
- 探索未知种族之osg类生物---呼吸分解之渲染遍历一
总结 前面我们基本上已经完成对ViewerBase::frame()函数的探究,只剩下renderingTraversals()渲染遍历的探究,虽然就剩下了一个函数,但是这却是最重要的,不可少的一个步 ...
- mysql开启调试日志general_log开启跟踪日志
general_log = 1 general_log_file = /tmp/umail_mysql.log 有时候,不清楚程序执行了什么sql语句,但是又要排除错误,找不到原因的情况下, 可以在m ...
- 安装配置python环境,并跑一个推荐系统的例子
1.官网下载python2.7,安装完后,在环境变量Path中加上这个路径 在控制台输入python,出现版本信息,就成功了. 2.我使用的是 pycharm,注册后,在 把自己的python.exe ...
- MyAdvice 填充方法(在原有方法上添加方法)
//applicationContext.xml配置文件 /UserServiceImp继承于UserService接口 <!-- 1 配置目标对象--> <bean nam ...
- log4net 写日志
转载地址:https://www.cnblogs.com/vichin/p/6022612.html //基本使用 https://www.cnblogs.com/genesis/p/498562 ...