返回介绍

Spring Boot 中使用 MyBatis 整合 Druid 多数据源

发布于 2025-02-18 23:57:57 字数 13244 浏览 0 评论 0 收藏 0

本文将讲述 spring boot + mybatis + druid 多数据源配置方案。

环境

CentOs7.3 安装 MySQL 5.7.19 二进制版本

Github 代码

代码我已放到 Github ,导入 spring-boot-mybatis 项目

github https://github.com/souyunku/spring-boot-examples/tree/master/spring-boot-mybatis

 项目结构

添加依赖

在项目中添加 mybatis,druid 依赖

点击预览 pom.xml

<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>druid</artifactId>
</dependency>
省略 更多

基础数据源

@Configuration
@EnableConfigurationProperties(DruidDbProperties.class)
@Import({DruidMonitConfig.class})
public abstract class AbstractDruidDBConfig {

  private Logger logger = LoggerFactory.getLogger(AbstractDruidDBConfig.class);

  @Resource
  private DruidDbProperties druidDbProperties;

  public DruidDataSource createDataSource(String url, String username, String password) {
    if (StringUtils.isEmpty(url)) {
      System.out.println(
          "Your database connection pool configuration is incorrect!" + " Please check your Spring profile");
      throw new ApplicationContextException("Database connection pool is not configured correctly");
    }

    DruidDataSource datasource = new DruidDataSource();

    datasource.setUrl(url);
    datasource.setUsername(username);
    datasource.setPassword(password);
    // datasource.setDriverClassName(
    // StringUtils.isEmpty(driverClassName) ?
    // druidDbProperties.getDriverClassName() : driverClassName);
    datasource.setInitialSize(druidDbProperties.getInitialSize());
    datasource.setMinIdle(druidDbProperties.getMinIdle());
    datasource.setMaxActive(druidDbProperties.getMaxActive());
    datasource.setMaxWait(druidDbProperties.getMaxWait());
    datasource.setTimeBetweenEvictionRunsMillis(druidDbProperties.getTimeBetweenEvictionRunsMillis());
    datasource.setMinEvictableIdleTimeMillis(druidDbProperties.getMinEvictableIdleTimeMillis());
    datasource.setValidationQuery(druidDbProperties.getValidationQuery());
    datasource.setTestWhileIdle(druidDbProperties.isTestWhileIdle());
    datasource.setTestOnBorrow(druidDbProperties.isTestOnBorrow());
    datasource.setTestOnReturn(druidDbProperties.isTestOnReturn());
    try {
      datasource.setFilters(druidDbProperties.getFilters());
    } catch (SQLException e) {
      logger.error("druid configuration initialization filter", e);
    }
    datasource.setConnectionProperties(druidDbProperties.getConnectionProperties());
    return datasource;

  }

  /**
   * 加载默认 mybatis xml 配置文件,并初始化分页插件
   *
   * @param dataSource
   * @return
   * @throws Exception
   */
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    return createSqlSessionFactory(dataSource, "classpath:mybatis/**/*.xml");
  }

  /**
   * 加载 mybatis xml 配置文件,并初始化分页插件
   *
   * @param dataSource    数据源
   * @param mapperLocations 自定义 xml 配置路径
   * @return
   * @throws Exception
   */
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource, String mapperLocations) throws Exception {
    return createSqlSessionFactory(dataSource, mapperLocations);
  }

  private SqlSessionFactory createSqlSessionFactory(DataSource dataSource, String mapperLocations) throws Exception {

    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    // mybatis 分页
    PageHelper pageHelper = new PageHelper();
    Properties props = new Properties();
    props.setProperty("dialect", "mysql");
    props.setProperty("reasonable", "true");
    props.setProperty("supportMethodsArguments", "true");
    props.setProperty("returnPageInfo", "check");
    props.setProperty("params", "count=countSql");
    pageHelper.setProperties(props); // 添加插件
    sqlSessionFactoryBean.setPlugins(new Interceptor[]{pageHelper});
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
    return sqlSessionFactoryBean.getObject();

  }
}

Druid 监控配置

@EnableConfigurationProperties(DruidDbProperties.class)
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class DruidMonitConfig {

  @Resource
  private DruidDbProperties druidDbProperties;

  @Bean
  public ServletRegistrationBean druidServlet() {
    ServletRegistrationBean reg = new ServletRegistrationBean();
    reg.setServlet(new StatViewServlet());
    reg.addUrlMappings("/druid/*");

    if (!StringUtils.isEmpty(druidDbProperties.getAllow())) {
      reg.addInitParameter("allow", druidDbProperties.getAllow()); // 白名单
    }
    if (!StringUtils.isEmpty(druidDbProperties.getDeny())) {
      reg.addInitParameter("deny", druidDbProperties.getDeny()); // 黑名单
    }
    reg.addInitParameter("loginUsername", druidDbProperties.getUsername());
    reg.addInitParameter("loginPassword", druidDbProperties.getPassword());
    return reg;
  }

  @Bean
  public FilterRegistrationBean filterRegistrationBean() {
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
    filterRegistrationBean.setFilter(new WebStatFilter());
    filterRegistrationBean.addUrlPatterns("/*");
    filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
    return filterRegistrationBean;
  }

  /**
   * 监听 Spring 1.定义拦截器 2.定义切入点 3.定义通知类
   *
   * @return
   */
  @Bean
  public DruidStatInterceptor druidStatInterceptor() {
    return new DruidStatInterceptor();
  }

  @Bean
  public JdkRegexpMethodPointcut druidStatPointcut() {
    JdkRegexpMethodPointcut druidStatPointcut = new JdkRegexpMethodPointcut();
    String patterns = "io.ymq.mybatis*";
    druidStatPointcut.setPatterns(patterns);
    return druidStatPointcut;
  }

  @Bean
  public Advisor druidStatAdvisor() {
    return new DefaultPointcutAdvisor(druidStatPointcut(), druidStatInterceptor());
  }
}

Druid 监控参数


@ConfigurationProperties(prefix = "druid")
public class DruidDbProperties {

  private String driverClassName = "com.mysql.jdbc.Driver";

  /**
   * 初始化时建立物理连接的个数。初始化发生在显示调用 init 方法,或者第一次 getConnection 时
   */
  private int initialSize = 10;

  /**
   * 最小连接池数量
   */
  private int minIdle = 50;

  /**
   * 最大连接池数量
   */
  private int maxActive = 300;

  /**
   * 获取连接时最大等待时间,单位毫秒。配置了 maxWait 之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置 useUnfairLock 属性为 true 使用非公平锁。
   */
  private int maxWait = 60000;

  /**
   * 有两个含义: 1)
   * Destroy 线程会检测连接的间隔时间,如果连接空闲时间大于等于 minEvictableIdleTimeMillis 则关闭物理连接。 2)
   * testWhileIdle 的判断依据,详细看 testWhileIdle 属性的说明
   */
  private int timeBetweenEvictionRunsMillis = 60000;

  /**
   * 连接保持空闲而不被驱逐的最长时间
   */
  private int minEvictableIdleTimeMillis = 3600000;

  /**
   * 用来检测连接是否有效的 sql,要求是一个查询语句,常用 select
   * 'x'。如果 validationQuery 为 null,testOnBorrow、testOnReturn、testWhileIdle 都不会其作用。
   */
  private String validationQuery = "SELECT USER()";

  /**
   * 建议配置为 true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery 检测连接是否有效。
   */
  private boolean testWhileIdle = true;

  /**
   * 申请连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
   */
  private boolean testOnBorrow = false;

  /**
   * 归还连接时执行 validationQuery 检测连接是否有效,做了这个配置会降低性能。
   */
  private boolean testOnReturn = false;

  /**
   * 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的 filter:stat 日志用的 filter:log4j
   * 防御 sql 注入的 filter:wall
   */
  private String filters = "mergeStat,config,wall";

  private String connectionProperties;

  /**
   * 白名单
   */
  private String allow;

  /**
   * 黑名单
   */
  private String deny;

  private String username = "admin";

  private String password = "admin";
	
	省略 get set
}

配置数据源 one

@Configuration
@EnableTransactionManagement
public class DBOneConfiguration extends AbstractDruidDBConfig {

  @Value("${ymq.one.datasource.url}")
  private String url;

  @Value("${ymq.one.datasource.username}")
  private String username;

  @Value("${ymq.one.datasource.password}")
  private String password;

  // 注册 datasourceOne
  @Bean(name = "datasourceOne", initMethod = "init", destroyMethod = "close")
  public DruidDataSource dataSource() {
    return super.createDataSource(url, username, password);
  }

  @Bean(name = "sqlSessionFactorYmqOne")
  public SqlSessionFactory sqlSessionFactory() throws Exception {
    return super.sqlSessionFactory(dataSource());
  }

  @Bean
  public PlatformTransactionManager transactionManager() throws SQLException {
    return new DataSourceTransactionManager(dataSource());
  }
}

配置数据源 two

@Configuration
@EnableTransactionManagement
public class DBOneConfiguration extends AbstractDruidDBConfig {

  @Value("${ymq.one.datasource.url}")
  private String url;

  @Value("${ymq.one.datasource.username}")
  private String username;

  @Value("${ymq.one.datasource.password}")
  private String password;

  // 注册 datasourceOne
  @Bean(name = "datasourceOne", initMethod = "init", destroyMethod = "close")
  public DruidDataSource dataSource() {
    return super.createDataSource(url, username, password);
  }

  @Bean(name = "sqlSessionFactorYmqOne")
  public SqlSessionFactory sqlSessionFactory() throws Exception {
    return super.sqlSessionFactory(dataSource());
  }

  @Bean
  public PlatformTransactionManager transactionManager() throws SQLException {
    return new DataSourceTransactionManager(dataSource());
  }
}

BaseDao one

@Repository
public class YmqOneBaseDao extends BaseDao {

  @Resource
  public void setSqlSessionFactorYmqOne(SqlSessionFactory sqlSessionFactory) {
    super.setSqlSessionFactory(sqlSessionFactory);
  }
}

BaseDao two

@Repository
public class YmqTwoBaseDao extends BaseDao {

  @Resource
  public void setSqlSessionFactorYmqTwo(SqlSessionFactory sqlSessionFactory) {
    super.setSqlSessionFactory(sqlSessionFactory);
  }
}

测试 Controller

@RestController
public class IndexController {

  private static final Logger LOG = LoggerFactory.getLogger(IndexController.class);

  @Autowired
  private YmqOneBaseDao ymqOneBaseDao;

  @Autowired
  private YmqTwoBaseDao ymqTwoBaseDao;

  @RequestMapping("/")
  public String index() throws Exception {

    List<TestOnePo> testOnePoList = null;

    testOnePoList = ymqOneBaseDao.selectList(new TestOnePo());
    for (TestOnePo item : testOnePoList) {
      LOG.info("数据源 ymqOneBaseDao :查询结果:{}", JSONObject.toJSONString(item));
    }

    List<TestTwoPo> testTwoPoList = null;

    testTwoPoList = ymqTwoBaseDao.selectList(new TestTwoPo());

    for (TestTwoPo item : testTwoPoList) {
      LOG.info("数据源 ymqTwoBaseDao:查询结果:{}", JSONObject.toJSONString(item));
    }

    String onePoList = JSONObject.toJSONString(testOnePoList);
    String twoPoList = JSONObject.toJSONString(testTwoPoList);

    return "数据源 ymqOneBaseDao :查询结果:" + onePoList + "<br/> 数据源 ymqTwoBaseDao :查询结果:" + twoPoList;
  }
}

参数配置

application.properties

#############SERVER CONFIG############
spring.application.name=ymq-mybatis-spring-boot

#数据源 one
ymq.one.datasource.url=jdbc:mysql://10.4.82.6:3306/ymq_one?useUnicode=true&characterEncoding=UTF-8
ymq.one.datasource.username=root
ymq.one.datasource.password=123456

#数据源 two
ymq.two.datasource.url=jdbc:mysql://10.4.82.6:3306/ymq_two?useUnicode=true&characterEncoding=UTF-8
ymq.two.datasource.username=root
ymq.two.datasource.password=123456

server.port=80
server.tomcat.max-threads=1000
server.tomcat.max-connections=2000

启动服务

@SpringBootApplication
@ComponentScan(value = {"io.ymq.mybatis"})
public class Startup {

  public static void main(String[] args) {
    SpringApplication.run(Startup.class, args);
  }
}

在页面上输入 http://localhost/ 可以看到 Controller 执行情况:

数据源 ymqOneBaseDao :查询结果:[{"id":1,"name":"测试","remark":"这是测试 ymq_one 数据库"}]
数据源 ymqTwoBaseDao :查询结果:[{"id":1,"name":"测试","remark":"这是测试 ymq_two 数据库"}]

在页面上输入 http://localhost/druid/ 可以看到监控到的 sql 语句执行情况:

 druid 监控

代码我已放到 Github ,导入 spring-boot-mybatis 项目

github https://github.com/souyunku/spring-boot-examples/tree/master/spring-boot-mybatis

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文