sping揭秘21Spring动态数据源的切换
Posted cutter_point
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sping揭秘21Spring动态数据源的切换相关的知识,希望对你有一定的参考价值。
对于多个数据源的时候,我们如何切换不同的数据源进行数据库的操作呢?
当然我们可以直接定义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揭秘21Spring动态数据源的切换的主要内容,如果未能解决你的问题,请参考以下文章