zoukankan      html  css  js  c++  java
  • 【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();
    //            }
    //        });
            
        }

     

     

     

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    哪种写法更好?<script></script> vs/or <script type=”text/javasript”></script>
    JS 脚本应该放在页面哪个位置 head body foot
    List<T> ForEach break
    嵌套JSON 取出name与value
    C# 改变图片尺寸(压缩),Image Resize
    tornado
    appachebench网站压力测试
    mysql分区分表
    redis的持久化存储,RDB与AOF
    MEMCACHE的内存管理和删除策略
  • 原文地址:https://www.cnblogs.com/cutter-point/p/9147805.html
Copyright © 2011-2022 走看看