加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

SSM框架之多数据源配置

发布时间:2020-12-15 07:12:58 所属栏目:Java 来源:网络整理
导读:多数据源的应用场景:主要是数据库拆分后,怎样让多个数据库结合起来来达到业务需求。 SSM框架(Spring+SpringMVC+MyBatis(MyBatis-Plus))是目前最常用的,此次仍然是maven工程。 关于这个多数据源例子,我已经上传到我的github上,地址为:https://github.com/

多数据源的应用场景:主要是数据库拆分后,怎样让多个数据库结合起来来达到业务需求。

SSM框架(Spring+SpringMVC+MyBatis(MyBatis-Plus))是目前最常用的,此次仍然是maven工程。

关于这个多数据源例子,我已经上传到我的github上,地址为:https://github.com/youcong1996/study_simple_demo.git

不过需要注意的是,分支为demo1,不是主分支,如图所示:

?

如果下面的示例,你们看不懂或者不能理解,可以git clone我的地址

在编程的世界里,简洁即完美。

如何实现多数据源?

一句话,三个类加xml配置即可达到这个目的。

?

一、编写三个类

?

AbstractDynamicDataSource.java

package com.blog.datasource;

import java.util.Map;

 javax.sql.DataSource;

 org.apache.commons.collections.MapUtils;
 org.slf4j.Logger;
 org.slf4j.LoggerFactory;
 org.springframework.beans.BeansException;
 org.springframework.context.ApplicationContext;
 org.springframework.context.ApplicationContextAware;
 org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * 动态数据源父类
 * @create ll
 * @update 
 * @updateDate 
 */
public abstract class AbstractDynamicDataSource<T extends DataSource> extends AbstractRoutingDataSource
                                                                                                implements
                                                                                                ApplicationContextAware {

     日志 */
    protected Logger logger = LoggerFactory.getLogger(getClass());
     默认的数据源KEY protected static final String DEFAULT_DATASOURCE_KEY = "defaultDataSource";

     数据源KEY-VALUE键值对 public Map<Object,Object> targetDataSources;

     spring容器上下文 private static ApplicationContext ctx;

    void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ctx = applicationContext;
    }

     ApplicationContext getApplicationContext() {
        return ctx;
    }

     Object getBean(String name) {
         ctx.getBean(name);
    }

    
     * @param targetDataSources the targetDataSources to set
     void setTargetDataSources(Map<Object,1)"> targetDataSources) {
        this.targetDataSources = targetDataSources;
        super.setTargetDataSources(targetDataSources);
        // afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
        .afterPropertiesSet();
    }

    
     * 创建数据源
     *  driverClassName 数据库驱动名称
     *  url 连接地址
     *  username 用户名
     *  password 密码
     * @return 数据源{@link T}
     * @Author : ll. create at 2017年3月27日 下午2:44:34
     abstract T createDataSource(String driverClassName,String url,String username,String password);

    
     * 设置系统当前使用的数据源
     * <p>数据源为空或者为0时,自动切换至默认数据源,即在配置文件中定义的默认数据源
     * @see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey()
     */
    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("【设置系统当前使用的数据源】");
        Map<String,Object> configMap = DBContextHolder.getDBType();
        logger.info("【当前数据源配置为:{}】",configMap);
        if (MapUtils.isEmpty(configMap)) {
             使用默认数据源
             DEFAULT_DATASOURCE_KEY;
        }
         判断数据源是否需要初始化
        this.verifyAndInitDataSource();
        logger.info("【切换至数据源:{}】" configMap.get(DBContextHolder.DATASOURCE_KEY);
    }

    
     * 判断数据源是否需要初始化
     * @Author : ll. create at 2017年3月27日 下午3:57:43
     void verifyAndInitDataSource() {
        Map<String,1)"> DBContextHolder.getDBType();
        Object obj = .targetDataSources.get(configMap.get(DBContextHolder.DATASOURCE_KEY));
        if (obj != null) {
            ;
        }
        logger.info("【初始化数据源】");
        T datasource = .createDataSource(configMap.get(DBContextHolder.DATASOURCE_DRIVER)
            .toString(),configMap.get(DBContextHolder.DATASOURCE_URL).toString(),configMap.get(DBContextHolder.DATASOURCE_USERNAME).toString(),configMap.get(DBContextHolder.DATASOURCE_PASSWORD).toString());
        .addTargetDataSource(configMap.get(DBContextHolder.DATASOURCE_KEY).toString(),datasource);
    }

    
     * 往数据源key-value键值对集合添加新的数据源
     *  key 新的数据源键
     *  dataSource 新的数据源
      addTargetDataSource(String key,T dataSource) {
        .targetDataSources.put(key,dataSource);
        super.setTargetDataSources(.targetDataSources);
        .afterPropertiesSet();
    }

}

?

DBContextHolder.java

 java.util.HashMap;
class DBContextHolder {
      数据源的KEY final String DATASOURCE_KEY = "DATASOURCE_KEY";
     数据源的URL final String DATASOURCE_URL = "DATASOURCE_URL" 数据源的驱动 final String DATASOURCE_DRIVER = "DATASOURCE_DRIVER" 数据源的用户名 final String DATASOURCE_USERNAME = "DATASOURCE_USERNAME" 数据源的密码 final String DATASOURCE_PASSWORD = "DATASOURCE_PASSWORD"final ThreadLocal<Map<String,Object>> contextHolder = new ThreadLocal<Map<String,Object>>();

    void setDBType(Map<String,1)"> dataSourceConfigMap) {
        contextHolder.set(dataSourceConfigMap);
    }

    static Map<String,1)"> getDBType() {
        Map<String,Object> dataSourceConfigMap = contextHolder.get();
        if (dataSourceConfigMap == ) {
            dataSourceConfigMap = new HashMap<String,1)">();
        }
         dataSourceConfigMap;
    }

     clearDBType() {
        contextHolder.remove();
    }
}

?

DruidDynamicDataSource.java

 java.sql.SQLException;
 java.util.List;

 org.apache.commons.lang3.StringUtils;

 com.alibaba.druid.filter.Filter;
 com.alibaba.druid.pool.DruidDataSource;


 * Druid数据源
 * @update 
 * @updateDate 
 class DruidDynamicDataSource extends AbstractDynamicDataSource<DruidDataSource> {

    boolean testWhileIdle = trueboolean testOnBorrow = falseboolean testOnReturn =  是否打开连接泄露自动检测
    boolean removeAbandoned =  连接长时间没有使用,被认为发生泄露时长
    long removeAbandonedTimeoutMillis = 300 * 1000 发生泄露时是否需要输出 log,建议在开启连接泄露检测时开启,方便排错
    boolean logAbandoned =  只要maxPoolPreparedStatementPerConnectionSize>0,poolPreparedStatements就会被自动设定为true,使用oracle时可以设定此值。
        private int maxPoolPreparedStatementPerConnectionSize = -1;

     配置监控统计拦截的filters
    private String filters;  监控统计:"stat" 防SQL注入:"wall" 组合使用: "stat,wall"
    private List<Filter> filterList;

    /*
     * 创建数据源
     * @see com.cdelabcare.pubservice.datasource.IDynamicDataSource#createDataSource(java.lang.String,java.lang.String,java.lang.String)
     public DruidDataSource createDataSource(String driverClassName,String password) {
        DruidDataSource parent = (DruidDataSource) .getApplicationContext().getBean(
            DEFAULT_DATASOURCE_KEY);
        DruidDataSource ds = new DruidDataSource();
        ds.setUrl(url);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setDriverClassName(driverClassName);
        ds.setInitialSize(parent.getInitialSize());
        ds.setMinIdle(parent.getMinIdle());
        ds.setMaxActive(parent.getMaxActive());
        ds.setMaxWait(parent.getMaxWait());
        ds.setTimeBetweenConnectErrorMillis(parent.getTimeBetweenConnectErrorMillis());
        ds.setTimeBetweenEvictionRunsMillis(parent.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(parent.getMinEvictableIdleTimeMillis());

        ds.setValidationQuery(parent.getValidationQuery());
        ds.setTestWhileIdle(testWhileIdle);
        ds.setTestOnBorrow(testOnBorrow);
        ds.setTestOnReturn(testOnReturn);

        ds.setRemoveAbandoned(removeAbandoned);
        ds.setRemoveAbandonedTimeoutMillis(removeAbandonedTimeoutMillis);
        ds.setLogAbandoned(logAbandoned);

                ds.setMaxPoolPreparedStatementPerConnectionSize(parent
            .getMaxPoolPreparedStatementPerConnectionSize());

         (StringUtils.isNotBlank(filters))
            try {
                ds.setFilters(filters);
            } catch (SQLException e) {
                throw  RuntimeException(e);
            }

        addFilterList(ds);
         ds;
    }

     addFilterList(DruidDataSource ds) {
        if (filterList != ) {
            List<Filter> targetList = ds.getProxyFilters();
            for (Filter add : filterList) {
                boolean found = ;
                 (Filter target : targetList) {
                     (add.getClass().equals(target.getClass())) {
                        found = ;
                        break;
                    }
                }
                if (!found)
                    targetList.add(add);
            }
        }
    }
}

?

二、修改配置文件

主要是修改spring-mybatis.xml

 <!-- 配置数据源 -->
    <bean name="defaultDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        property ="url" value="${jdbc_url}"/>
        ="username"="${jdbc_username}"="password"="${jdbc_password}"/>

         初始化连接大小 -->
        ="initialSize"="0" 连接池最大使用连接数量 ="maxActive"="20" 连接池最大空闲 ="maxIdle" 连接池最小空闲 ="minIdle" 获取连接最大等待时间 ="maxWait"="60000"="validationQuery"="${validationQuery}"="testOnBorrow"="false"="testOnReturn"="testWhileIdle"="true" 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 ="timeBetweenEvictionRunsMillis" 配置一个连接在池中最小生存的时间,单位是毫秒 ="minEvictableIdleTimeMillis"="25200000" 打开removeAbandoned功能 ="removeAbandoned" 1800秒,也就是30分钟 ="removeAbandonedTimeout"="1800" 关闭abanded连接时输出错误日志 ="logAbandoned" 监控数据库 ="filters"="mergeStat"/>
    </bean>


   id="druidDynamicDataSource"="com.blog.datasource.DruidDynamicDataSource"="defaultTargetDataSource" ref="defaultDataSource" ="targetDataSources">
            map>
                entry key value-ref="defaultDataSource"/>
                 这里还可以加多个dataSource -->
            property>
    > 

 Spring整合Mybatis,更多查看文档:http://mp.baomidou.com ="sqlSessionFactory"="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
       ="dataSource"="druidDynamicDataSource"  自动扫描Mapping.xml文件 ="mapperLocations"="classpath:mybatis/system/*.xml"="configLocation"="classpath:mybatis/mybatis-config.xml"="typeAliasesPackage"="com.blog.entity"="plugins"array 分页插件配置 -->
                ="paginationInterceptor"="com.baomidou.mybatisplus.plugins.PaginationInterceptor" 全局配置注入 ="globalConfig"="globalConfig" >

     配置事务管理 ="transactionManager"="org.springframework.jdbc.datasource.DataSourceTransactionManager"="druidDynamicDataSource">

?

三、单元测试

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.blog.datasource.DBContextHolder;
import com.blog.entity.User;
import com.blog.mapper.PostDao;
import com.blog.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring.xml")
public class BlogTest {


    @Autowired
    private UserService ud;
    
    @Test
    public void testName() throws Exception {
        MapString> map = new HashMap>();
        map.put(DBContextHolder.DATASOURCE_KEY,"localhost");
        map.put(DBContextHolder.DATASOURCE_DRIVER,"com.mysql.jdbc.Driver");
        map.put(DBContextHolder.DATASOURCE_URL,"jdbc:mysql://127.0.0.1:3306/blog_test?useUnicode=true&characterEncoding=UTF-8");
        map.put(DBContextHolder.DATASOURCE_USERNAME,"root");
        map.put(DBContextHolder.DATASOURCE_PASSWORD,"1234");
        DBContextHolder.setDBType(map);
        
        
        ListUser list = ud.selectList(null);
        for (User user : list) {
            System.out.println(user);
        }
    }


}

?

测试后,控制台如图:

?

小结:

其实配置多数据源有很多方式,有aop,也有配置多个bean的方式,当然了,只要能达到目的就是王道,当然了,我也强调一点,不是实现完就不管了,背后的为什么比只要实现就好更重要。

其实,有一点我想说的是,有些时候遇到难题,最好的方式是迎面而上解决这个问题,而不是逃避或者独自焦躁。同时直面问题,也是解决焦躁的最好方式。这个我已经深有体会了。

另外补充到,上传至github上的多数据源示例同时也是ssm框架的搭建。有哪位朋友不会搭建框架,可以参考我的这个。希望能对你们有什么帮助。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读