SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询

在上一章中文,我使用springboot工程整合了mp,以及mp基础使用,代码生成器使用,Druid 数据监控等,但仅限于单数据源 请戳SpringBoot整合Mybatis-plus(一)基本使用与自定义模板代码生成器

因为很多时候,在开发中,并不会只有一个数据库,在保存一条数据的时候,可能需要向几个数据库保存,那么当发生异常时候,就会面临事务问题了,需要将保存的数据进行回滚,所以,本文开始讲解,整合 Mp 多数据源下的druid监控以及事务处理

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第1张图片

一.添加新的依赖

        
        
            org.springframework.boot
            spring-boot-starter-jta-atomikos
        
        
        
            mysql
            mysql-connector-java
            5.1.47
        

二.多数据源情况下的YML配置

spring:
  datasource:
    type: com.alibaba.druid.pool.xa.DruidXADataSource
    druid:
      #第一个数据源
      one:
        name: oneDataSource
        url: jdbc:mysql://127.0.0.1:3306/mybatis-plus?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
        # 下面为连接池的补充设置,应用到上面所有数据源中
        # 初始化大小,最小,最大
        initialSize: 5
        minIdle: 5
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1
        validationQueryTimeout: 10000
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,wall
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
        # 合并多个DruidDataSource的监控数据
        useGlobalDataSourceStat: true

      #第二个数据源
      two:
        name: twoDataSource
        url: jdbc:mysql://127.0.0.1:3306/mybatis-plus2?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
        # 下面为连接池的补充设置,应用到上面所有数据源中
        # 初始化大小,最小,最大
        initialSize: 5
        minIdle: 5
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1
        validationQueryTimeout: 10000
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,wall
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
        # 合并多个DruidDataSource的监控数据
        useGlobalDataSourceStat: true
      #第三个数据源
      three:
        name: threeDataSource
        url: jdbc:mysql://127.0.0.1:3306/mybatis-plus3?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
        # 下面为连接池的补充设置,应用到上面所有数据源中
        # 初始化大小,最小,最大
        initialSize: 5
        minIdle: 5
        maxActive: 20
        # 配置获取连接等待超时的时间
        maxWait: 60000
        # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
        timeBetweenEvictionRunsMillis: 60000
        # 配置一个连接在池中最小生存的时间,单位是毫秒
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1
        validationQueryTimeout: 10000
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        # 打开PSCache,并且指定每个连接上PSCache的大小
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 20
        filters: stat,wall
        # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
        # 合并多个DruidDataSource的监控数据
        useGlobalDataSourceStat: true
  jta:
    atomikos:
      properties:
        log-base-dir: tx-logs
    transaction-manager-id: txManager    #默认取计算机的IP地址 需保证生产环境值唯一

三.配置Bean形式读取数据库信息

由于配置了one two three 三个数据源 已经是改变了springBoot 对Mysql 连接读取的配置路径,所以呢,我们需要自定义数据源的位置,在启动项目时才能成功读取数据库相关信息

无论使用Mybatis 还是mp 在访问数据库时都需要 数据源 , SqlSessionFactory,SqlSessionTemplate

首先需要从配置文件中,获取到数据源信息,以及Druid需要监控的信息

读取我们配置文件中数据库路径 生成对象并配置成Bean 交由spring管理

附上我完整代码

DruidConfig
package com.leilei.config;

import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.jta.atomikos.AtomikosDataSourceBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;

import java.util.Properties;

/**
 * 多数据源和Druid配置
 *
 * @author leilei
 */
@Configuration
public class DruidConfig {

    /**
     * 数据源1配置   使用AtomikosDataSourceBean 支持多数据源事务
     *
     * @param env
     * @return Primary 指定主库  (必须指定一个主库 否则会报错)
     */
    @Bean(name = "MybatisPlusOneDataSource")
    @Primary
    @Autowired
    public AtomikosDataSourceBean oneDataSource(Environment env) {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = build(env, "spring.datasource.druid.one.");
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName("oneDataSource");
        ds.setPoolSize(5);
        ds.setXaProperties(prop);
        return ds;
    }

    /**
     * 数据源2配置  使用AtomikosDataSourceBean 支持多数据源事务
     *
     * @param env
     * @return
     */
    @Autowired
    @Bean(name = "MybatisPlusTwoDataSource")
    public AtomikosDataSourceBean twoDataSource(Environment env) {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = build(env, "spring.datasource.druid.two.");
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName("twoDataSource");
        ds.setPoolSize(5);
        ds.setXaProperties(prop);
        return ds;
    }

    @Autowired
    @Bean(name = "MybatisPlusThreeDataSource")
    public AtomikosDataSourceBean threeDataSource(Environment env) {
        AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
        Properties prop = build(env, "spring.datasource.druid.three.");
        ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        ds.setUniqueResourceName("threeDataSource");
        ds.setPoolSize(5);
        ds.setXaProperties(prop);
        return ds;
    }

//    /**
//     * 注入事物管理器
//     * @return
//     */
//    @Bean(name = "leijta")
//    public JtaTransactionManager regTransactionManager () {
//        UserTransactionManager userTransactionManager = new UserTransactionManager();
//        UserTransaction userTransaction = new UserTransactionImp();
//        return new JtaTransactionManager(userTransaction, userTransactionManager);
//    }

    /**
     * 从配置文件中加载数据源信息
     *
     * @param env
     * @param prefix
     * @return
     */
    private Properties build(Environment env, String prefix) {
        Properties prop = new Properties();
        prop.put("url", env.getProperty(prefix + "url"));
        prop.put("username", env.getProperty(prefix + "username"));
        prop.put("password", env.getProperty(prefix + "password"));
        prop.put("driverClassName", env.getProperty(prefix + "driverClassName", ""));
        prop.put("initialSize", env.getProperty(prefix + "initialSize", Integer.class));
        prop.put("maxActive", env.getProperty(prefix + "maxActive", Integer.class));
        prop.put("minIdle", env.getProperty(prefix + "minIdle", Integer.class));
        prop.put("maxWait", env.getProperty(prefix + "maxWait", Integer.class));
        prop.put("poolPreparedStatements", env.getProperty(prefix + "poolPreparedStatements", Boolean.class));
        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
        prop.put("maxPoolPreparedStatementPerConnectionSize",
                env.getProperty(prefix + "maxPoolPreparedStatementPerConnectionSize", Integer.class));
        prop.put("validationQuery", env.getProperty(prefix + "validationQuery"));
        prop.put("validationQueryTimeout", env.getProperty(prefix + "validationQueryTimeout", Integer.class));
        prop.put("testOnBorrow", env.getProperty(prefix + "testOnBorrow", Boolean.class));
        prop.put("testOnReturn", env.getProperty(prefix + "testOnReturn", Boolean.class));
        prop.put("testWhileIdle", env.getProperty(prefix + "testWhileIdle", Boolean.class));
        prop.put("timeBetweenEvictionRunsMillis",
                env.getProperty(prefix + "timeBetweenEvictionRunsMillis", Integer.class));
        prop.put("minEvictableIdleTimeMillis", env.getProperty(prefix + "minEvictableIdleTimeMillis", Integer.class));
        prop.put("filters", env.getProperty(prefix + "filters"));
        return prop;
    }

    /**
     * druid访问配置
     *
     * @return
     */
    @Bean
    public ServletRegistrationBean druidServlet() {
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
        //控制台管理用户,加入下面2行 进入druid后台就需要登录
        servletRegistrationBean.addInitParameter("loginUsername", "leilei");
        servletRegistrationBean.addInitParameter("loginPassword", "123456");
        return servletRegistrationBean;
    }

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

    @Bean
    public StatFilter statFilter() {
        StatFilter statFilter = new StatFilter();
        //slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。
        statFilter.setLogSlowSql(true);
        //SQL合并配置
        statFilter.setMergeSql(true);
        //slowSqlMillis的缺省值为3000,也就是3秒。
        statFilter.setSlowSqlMillis(1000);
        return statFilter;
    }

    @Bean
    public WallFilter wallFilter() {
        WallFilter wallFilter = new WallFilter();
        //允许执行多条SQL
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);
        wallFilter.setConfig(config);
        return wallFilter;
    }
}

在DruidConfig中看到了 每个数据源的Bean 都是 AtomikosDataSourceBean 类型 ,只要数据源是此类型,在多个数据源下,打上Spring自带的事务注解@Transactional 即可

AtomikosDataSourceBean类型数据源
/**
 * 数据源1配置   使用AtomikosDataSourceBean 支持多数据源事务
 *
 * @param env
 * @return Primary 指定主库  (必须指定一个主库 否则会报错)
 */
@Bean(name = "MybatisPlusOneDataSource")
@Primary
@Autowired
public AtomikosDataSourceBean oneDataSource(Environment env) {
    AtomikosDataSourceBean ds = new AtomikosDataSourceBean();
    Properties prop = build(env, "spring.datasource.druid.one.");
    ds.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
    ds.setUniqueResourceName("oneDataSource");
    ds.setPoolSize(5);
    ds.setXaProperties(prop);
    return ds;
}
配置每个数据源的sqlsessionfactory sqlSessionTemplate

这里贴上我的第一个数据源的sqlsessionfactory sqlSessionTemplate,无论还有多少数据源,按照我这个配置即可,改改包路径,@Qualifier选择对应数据源 ,改改mpper.xml路径即可

需注意的是,必须有一个数据源的所有相关(datasource,sqlsessionfactory sqlSessionTemplate)要使用**@Primary**注解指定一个主库,否则会启动报错。

package com.leilei.config;

import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author leilei
 */
@Configuration
@MapperScan(basePackages = "com.leilei.mapper.one", sqlSessionFactoryRef = "oneSqlSessionFactory")
public class OneDataSourceConfig {

    @Primary
    @Bean(name = "oneSqlSessionFactory")
    public SqlSessionFactory sqlSessionFactory(@Qualifier("MybatisPlusOneDataSource") DataSource dataSource) throws Exception {
        //配置myabtisSqlSession
        MybatisSqlSessionFactoryBean sessionFactoryBean = new MybatisSqlSessionFactoryBean();
        // 指明mapper.xml位置(配置文件中指明的xml位置会失效用此方式代替,具体原因未知)
        sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:com/leilei/mapper/one/*/*Mapper.xml"));
        // 指明实体扫描(多个package用逗号或者分号分隔)
        sessionFactoryBean.setTypeAliasesPackage("com.leilei.entity.one");

        MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
        // mybatisConfiguration.setJdbcTypeForNull(JdbcType.NULL);
        //驼峰
        mybatisConfiguration.setMapUnderscoreToCamelCase(true);
        //是否开启缓存
        mybatisConfiguration.setCacheEnabled(false);
        //多数据源下分页模式
        mybatisConfiguration.addInterceptor(new PaginationInterceptor());
        // 配置打印sql语句
        mybatisConfiguration.setLogImpl(StdOutImpl.class);
        sessionFactoryBean.setConfiguration(mybatisConfiguration);
        //数据源注入
        sessionFactoryBean.setDataSource(dataSource);
        return sessionFactoryBean.getObject();
    }

    @Primary
    @Bean(name = "oneSqlSessionTemplate")
    public SqlSessionTemplate sqlSessionTemplate(@Qualifier("oneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

其他数据源的sqlsessionfactory sqlSessionTemplate 照着我这个配置即可 ,去掉 @Primary注解 注意Bean 名称设为唯一。

其实到这里 多数据源下的事务管理以及Druid监控已经是整合完毕了

四.本文需注意的点

1.导入atomikos 依赖
2.mysql 驱动为5 版本
3.包路径划分

在使用代码生成器时,将每个数据源的mapper, entity ,mapper.xml包路径划分开来

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第2张图片

4.多数据源也需要@Transactional 注解

五.多数据源事务测试

事务测试

一个方法中,调用多个数据源插入数据,并中途制造异常,报错后,查看数据回滚·

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第3张图片

body 为我封装的一个对象,因为本文设计三个对象,我准备向三个数据源一起插入数据测试事务
SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第4张图片

操作数据库

    @Override
    @Transactional //数据源配置了AtomikosDataSourceBean 再打上此注解即可做到多数据源事务控制
    public Map insertAto(BodyVo bodyVo) {
        userMapper.insert(bodyVo.getUser());
        userRoleMapper.insert(bodyVo.getUserRole());
        int a = 1 / 0; //制造异常
        roleMapper.insert(bodyVo.getRole());
        HashMap map = new HashMap<>();
        map.put("user", bodyVo.getUser());
        map.put("user_role", bodyVo.getUserRole());
        map.put("role", bodyVo.getRole());
        return map;
    }

未开始测试前,数据库数据

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第5张图片

运行测试结果,可以看到是报了异常

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第6张图片

那么,此时再查看数据数据,刷新后,发现数据并未改变

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第7张图片

那么说明,多数据源下的事务问题已经解决了!!!!!

多数据源下Mp 分页以及跨表查询

本文是将一个多对多关系的三张表放在了三个库中

依User 为第一查询视角
SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第8张图片

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第9张图片

本文中usermapper.MoreDatasourceFindAll 是自定义的方法,为了有分页效果必须构造一个Page对象,将Page对象与查询条件一起作为参数传到UserMapper.interface中,再有mapper.xml操作数据库

    IPage MoreDatasourceFindAll(Page page, @Param("id") Long id);
  
    
        
        
        
        
            
            
        
    
    

SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第10张图片
查看Druid监控
发现三个数据源均在此排列
SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第11张图片
Url监控
SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询_第12张图片
结语
到这里,本次项目整合就结束了,附上我的项目源码
SpringBoot整合Mybatis-plus(二) 多数据源Druid监控,Atomikos处理事务,跨库连表查询

你可能感兴趣的:(Mybatis,spring-boot,mybatis,spring,java)