springboot+mybatis+jta+atomikos解决多数据源事务问题

博客介绍了使用springboot、mybatis、jta和atomikos解决多数据源事务问题。先搭建多数据源环境,包括添加依赖、配置文件等,完成后进行测试。接着进行多数据源事务回滚测试,发现默认配置下部分库不回滚。最后使用springboot+jta+atomikos修改相关配置解决事务管理问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

springboot+mybatis+jta+atomikos解决多数据源事务问题

一.多数据源环境搭建

项目目录架构如下

1558418474501

1558418891286

1558418911373

  • 添加pom依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.5.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sgz</groupId>
    <artifactId>atomikos</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>atomikos</name>
    <description>springboot+jta+atomikos解决多数据源事务问题</description>

    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <!--项目使用mysql版本,默认是8.0.15 -->
        <mysql.version>5.1.40</mysql.version>
        <!--项目使用Tomcat版本 -->
        <tomcat.version>8.5.38</tomcat.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.14</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--mysql驱动包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--jdbc启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <!-- 分页插件 -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.10</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  • 配置文件application.yml
server:
  port: 8001
  
spring:
  mvc:
    date-format: yyyy-MM-dd HH:mm:ss #修改表单提交到后台的日期格式

  #数据源配置
  datasource:
    db1:
      type: com.alibaba.druid.pool.DruidDataSource #指定自定义的数据源类型,如果这里不指定,默认使用的是HikariDataSource
      username: root
      password: 12345678
      url: jdbc:mysql://192.168.5.130:3306/db1
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 8 #数据库连接池初始化连接大小
      minIdle: 5 #数据库连接池最小连接池数量
      maxActive: 20 #数据库连接池最大连接池数量
      maxWait: 60000 #获取连接时最大等待时间,单位毫秒
      timeBetweenEvictionRunsMillis: 60000 #有两个含义:1) Destroy线程会检测连接的间隔时间,即配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 2) testWhileIdle的判断依据;
      minEvictableIdleTimeMillis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒
      validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
      testWhileIdle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      testOnBorrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      poolPreparedStatements: false #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,分库分表较多的数据库,建议配置为false。
      maxPoolPreparedStatementPerConnectionSize: -1 #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      #监控配置
      filters: stat,wall,slf4j
      useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据,多数据源
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #druid.stat.mergeSql=true 合并执行的相同sql,避免因为参数不同而统计多条sql语句, druid.stat.slowSqlMillis=500 用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢

    db2:
      type: com.alibaba.druid.pool.DruidDataSource #指定自定义的数据源类型,如果这里不指定,默认使用的是HikariDataSource
      username: root
      password: 12345678
      url: jdbc:mysql://192.168.5.130:3306/db2
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 18 #数据库连接池初始化连接大小
      minIdle: 15 #数据库连接池最小连接池数量
      maxActive: 120 #数据库连接池最大连接池数量
      maxWait: 60000 #获取连接时最大等待时间,单位毫秒
      timeBetweenEvictionRunsMillis: 60000 #有两个含义:1) Destroy线程会检测连接的间隔时间,即配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 2) testWhileIdle的判断依据;
      minEvictableIdleTimeMillis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒
      validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
      testWhileIdle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      testOnBorrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      poolPreparedStatements: false #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,分库分表较多的数据库,建议配置为false。
      maxPoolPreparedStatementPerConnectionSize: -1 #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      #监控配置
      filters: stat,wall,slf4j
      useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据,多数据源
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #druid.stat.mergeSql=true 合并执行的相同sql,避免因为参数不同而统计多条sql语句, druid.stat.slowSqlMillis=500 用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢

#mybatis配置文件
mybatis:
  #mapper-locations: classpath:mybatis/mapper/db1/*.xml #mybatis的sql的mapper接口映射文件(单数据源配置使用) 多数据源在对应的配置类中配置使用,这里配置不起作用了(比如DataSource1Config类中)
  #type-aliases-package: com.sgz.atomikos.**.entity #对应实体类的路径(可以不用配置)
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名转换

#pagehelper分页插件
pagehelper:
  helperDialect: mysql
  reasonable: true  #pageHelper里面自带的一个功能,叫做reasonable分页参数合理化,3.3.0以上版本可用,默认是false。 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages(超过总数时)会查询最后一页;禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据
  supportMethodsArguments: true
  params: count=countSql;

#日志配置
logging:
  level:
    root: info
    com.sgz.atomikos: debug
  file: D:/logs/springboot.log

  • 数据源配置类(这里有两个数据源,所以就有2个配置类)
package com.sgz.atomikos.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
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 org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Auther:shigzhcom.sgz.atomikos.test1下面的mapper访问的是db1数据库
 * @Description: 这个是主数据源,所以要加@Primary注解
 * @create 2019/2/6 16:55
 */
@Configuration
@MapperScan(basePackages = "com.sgz.atomikos.test1", sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class DataSource1Config {
    //绑定数据源配置
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    @Bean
    public DataSource db1DataSource() {
        return new DruidDataSource();
    }

    /**
     * 创建Mybatis的连接会话工厂实例
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    @Primary
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //这里加载对应的mybatis的xml配置文件,application.yml里就不用配置了,即使配置了也不起作用
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/mapper/db1/*.xml"));
        return bean.getObject();
    }

    /**
     * 创建该数据源的事务管理
     * @param dataSource
     * @return
     */
    @Bean
    @Primary
    public DataSourceTransactionManager db1TransactionManager(@Qualifier("db1DataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    @Primary
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}







package com.sgz.atomikos.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;

/**
 * @Description:com.sgz.atomikos.test2下面的mapper访问的是db2数据库
 * @Auther:shigzh
 * @create 2019/5/20 17:42
 */
@Configuration
@MapperScan(basePackages = "com.sgz.atomikos.test2", sqlSessionTemplateRef = "db2SqlSessionTemplate")
public class DataSource2Config {
    //绑定数据源配置
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    @Bean
    public DataSource db2DataSource() {
        return new DruidDataSource();
    }

    /**
     * 创建Mybatis的连接会话工厂实例
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //这里加载对应的mybatis的xml配置文件,application.yml里就不用配置了,即使配置了也不起作用
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/mapper/db2/*.xml"));
        return bean.getObject();
    }

    /**
     * 创建该数据源的事务管理
     * @param dataSource
     * @return
     */
    @Bean
    public DataSourceTransactionManager db2TransactionManager(@Qualifier("db2DataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public SqlSessionTemplate db2SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

  • druid数据源监控配置类
package com.sgz.atomikos.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
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 javax.servlet.Filter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description: 单独整个配置,配置druid数据源监控
 * @Auther:shigzh
 * @create 2019/5/21 13:48
 */
@Configuration
public class DruidConfig {
    /**
     * 配置Druid监控
     * 1. 配置一个管理后台的Servlet
     * 2. 配置一个监控的filter
     */
    @Bean // 1. 配置一个管理后台的Servlet
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        //StatViewServlet是 配置管理后台的servlet
        ServletRegistrationBean<StatViewServlet> bean =
                new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        //配置初始化参数
        Map<String, String> initParam = new HashMap<>();
        //访问的用户名密码
        initParam.put(StatViewServlet.PARAM_NAME_USERNAME, "root");
        initParam.put(StatViewServlet.PARAM_NAME_PASSWORD, "123");
        //允许访问的ip,默认所有ip访问
        initParam.put(StatViewServlet.PARAM_NAME_ALLOW, "");
        //禁止访问的ip
        initParam.put(StatViewServlet.PARAM_NAME_DENY, "192.168.10.1");
        //监控配置界面中 是否能够重置数据(点了重置所有监控数据就没有了)
        //initParam.put(StatViewServlet.PARAM_NAME_RESET_ENABLE,"false");
        initParam.put(StatViewServlet.PARAM_NAME_RESET_ENABLE,"true");
        bean.setInitParameters(initParam);
        return bean;
    }
    //2. 配置一个监控的filter
    @Bean
    public FilterRegistrationBean<Filter> filter() {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());
        //配置初始化参数
        Map<String, String> initParam = new HashMap<>();
        //排除请求
        initParam.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
        bean.setInitParameters(initParam);
        //拦截所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

  • test1目录下的mapper访问db1库数据源
package com.sgz.atomikos.test1.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/21 9:43
 */
@Data
public class User1Entity implements Serializable {
    //按alt+enter键 生成序列化id
    private static final long serialVersionUID = -3932087591641353874L;
    private Integer id; //主键
    private String userName; //名称
    private Date date;
    private String dbSource; // 来自哪个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库
}

package com.sgz.atomikos.test1.mapper;

import com.sgz.atomikos.test1.entity.User1Entity;

public interface User1Mapper {

    boolean addUser(User1Entity user1Entity);
}
import com.sgz.atomikos.test1.entity.User1Entity;
import com.sgz.atomikos.test1.mapper.User1Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/21 9:52
 */
@Transactional
@Service
public class User1Service {
    @Autowired
    private User1Mapper user1Mapper;

    public void testAdd(){
        User1Entity user1Entity = new User1Entity();
        user1Entity.setUserName("user1");
        user1Mapper.addUser(user1Entity);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.sgz.atomikos.test1.mapper.User1Mapper">
    <insert id="addUser" parameterType="com.sgz.atomikos.test1.entity.User1Entity">
       INSERT INTO user1(userName, dbSource,date) VALUES(#{userName}, DATABASE(),now());
    </insert>

</mapper>
  • test2目录下的mapper访问db2库数据源
package com.sgz.atomikos.test2.entity;

import lombok.Data;

import java.util.Date;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/21 9:46
 */
@Data
public class User2Entity {
    //按alt+enter键 生成序列化id
    private static final long serialVersionUID = -3932017591641353874L;
    private Integer id; //主键
    private String userName; //名称
    private Date date;
    private String dbSource; // 来自哪个数据库,因为微服务架构可以一个服务对应一个数据库,同一个信息被存储到不同数据库
}
package com.sgz.atomikos.test2.mapper;

import com.sgz.atomikos.test2.entity.User2Entity;

public interface User2Mapper {

    boolean addUser(User2Entity user2Entity);
}
package com.sgz.atomikos.test2.service;

import com.sgz.atomikos.test2.entity.User2Entity;
import com.sgz.atomikos.test2.mapper.User2Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/21 9:52
 */
@Transactional
@Service
public class User2Service {
    @Autowired
    private User2Mapper user2Mapper;

    public void testAdd(){
        User2Entity user2Entity = new User2Entity();
        user2Entity.setUserName("user2");
        user2Mapper.addUser(user2Entity);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.sgz.atomikos.test2.mapper.User2Mapper">
    <insert id="addUser" parameterType="com.sgz.atomikos.test2.entity.User2Entity">
       INSERT INTO user2(userName, dbSource,date) VALUES(#{userName}, DATABASE(),now());
    </insert>

</mapper>
  • 启动类
package com.sgz.atomikos;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//扫描mybatis使用的mapper接口
//@MapperScan(value="com.sgz.**.mapper")  //扫描mybatis的mapper文件,多数据源就不在这里配置扫描mapper了
@SpringBootApplication
public class AtomikosApplication {

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

}

  • controller类
package com.sgz.atomikos.controller;

import com.sgz.atomikos.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/20 17:16
 */
@RestController
public class TestController {
    @Autowired
    private TestService testService;

    @RequestMapping("/testAdd")
    public void testAdd(){
        testService.testAdd();
    }
}

package com.sgz.atomikos.service;

import com.sgz.atomikos.test1.service.User1Service;
import com.sgz.atomikos.test2.service.User2Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @Description:
 * @Auther:shigzh
 * @create 2019/5/20 17:16
 */
@Transactional
@Service
public class TestService {
    @Autowired
    private User1Service user1Service;
    @Autowired
    private User2Service user2Service;

    public void testAdd(){
        user1Service.testAdd();
        user2Service.testAdd();
    }
}

1558419138821

1558419154781

  • 检查druid数据库连接池配置文件参数是否生效

    1558419530050

如上图如果dataSource里的属性与application.yml中的配置一致,说明生效

     initialSize: 8 #数据库连接池初始化连接大小
      minIdle: 5 #数据库连接池最小连接池数量
      maxActive: 20 #数据库连接池最大连接池数量
      maxWait: 60000 #获取连接时最大等待时间,单位毫秒
      timeBetweenEvictionRunsMillis: 60000 #有两个含义:1) Destroy线程会检测连接的间隔时间,即配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 2) testWhileIdle的判断依据;
      minEvictableIdleTimeMillis: 300000 
二.多数据源事务回滚测试
@Transactional
@Service
public class TestService {
    @Autowired
    private User1Service user1Service;
    @Autowired
    private User2Service user2Service;

    public void testAdd(){
        user1Service.testAdd();
        user2Service.testAdd();
        int a =1/0;
    }
}

结果:db1发生回滚,db2没有发生回滚

原因:@Transactional默认使用的是带有@Primary的事务管理器db1TransactionManager

这里注解如果修改成@Transactional(value=“db2TransactionManager”)

@Transactional(value="db2TransactionManager")
@Service
public class TestService {
    @Autowired
    private User1Service user1Service;
    @Autowired
    private User2Service user2Service;

    public void testAdd(){
        user1Service.testAdd();
        user2Service.testAdd();
        int a =1/0;
    }
}

结果:db2发生回滚,db1没有发生回滚

三.多数据源事务问题解决

这里使用springboot+jta+atomikos 来解决多数据源事务管理问题

修改DataSource1Config

package com.sgz.atomikos.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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;

/**
 * @Auther:shigzh
 * @Description: com.sgz.atomikos.test1下面的mapper访问的是db1数据库
 * 这个是主数据源,所以要加@Primary注解
 * @create 2019/2/6 16:55
 */
@Configuration
@MapperScan(basePackages = "com.sgz.atomikos.test1", sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class DataSource1Config {

    /**
     * 创建Mybatis的连接会话工厂实例
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    @Primary
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //这里加载对应的mybatis的xml配置文件,application.yml里就不用配置了,即使配置了也不起作用
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/mapper/db1/*.xml"));
        return bean.getObject();
    }


    @Bean
    @Primary
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }

}







修改DataSource2Config

package com.sgz.atomikos.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @Description:com.sgz.atomikos.test2下面的mapper访问的是db2数据库
 * @Auther:shigzh
 * @create 2019/5/20 17:42
 */
@Configuration
@MapperScan(basePackages = "com.sgz.atomikos.test2", sqlSessionTemplateRef = "db2SqlSessionTemplate")
public class DataSource2Config {

    /**
     * 创建Mybatis的连接会话工厂实例
     * @param dataSource
     * @return
     * @throws Exception
     */
    @Bean
    public SqlSessionFactory db2SqlSessionFactory(@Qualifier("db2DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //这里加载对应的mybatis的xml配置文件,application.yml里就不用配置了,即使配置了也不起作用
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mybatis/mapper/db2/*.xml"));
        return bean.getObject();
    }


    @Bean
    public SqlSessionTemplate db2SqlSessionTemplate(@Qualifier("db2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

修改DruidConfig

package com.sgz.atomikos.config;

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.atomikos.icatch.jta.UserTransactionImp;
import com.atomikos.icatch.jta.UserTransactionManager;
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 org.springframework.transaction.jta.JtaTransactionManager;

import javax.servlet.Filter;
import javax.sql.DataSource;
import javax.transaction.UserTransaction;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 * 网上查找都说 atomikos这套方案性能太差了,做性能测试的时候并发完全压不上去,高并发的系统千万不要用。简单了解学习使用
 *
 * XA 是一个分布式事务协议,由Tuxedo 提出,所以分布式事务也称为XA 事务
 * @Description: 单独整个配置,配置druid数据源监控
 * @Auther:shigzh
 * @create 2019/5/21 13:48
 */
@Configuration
public class DruidConfig {

    @Autowired
    private  Environment env;

    //绑定数据源配置
    @Primary
    @Bean
    public DataSource db1DataSource() {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();//分布式数据源(AtomikosNonXADataSourceBean 非分布式数据源)
        Properties prop = build("spring.datasource.db1.");
        dataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        dataSourceBean.setUniqueResourceName("db1");//该值要唯一
        dataSourceBean.setPoolSize(5);
        dataSourceBean.setXaProperties(prop);
        return dataSourceBean;
    }

    @Bean
    public DataSource db2DataSource() {
        AtomikosDataSourceBean dataSourceBean = new AtomikosDataSourceBean();//分布式数据源
        Properties prop = build("spring.datasource.db2.");
        dataSourceBean.setXaDataSourceClassName("com.alibaba.druid.pool.xa.DruidXADataSource");
        dataSourceBean.setUniqueResourceName("db2"); //该值要唯一
        dataSourceBean.setPoolSize(5);
        dataSourceBean.setXaProperties(prop);
        return dataSourceBean;
    }

    private Properties build(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 + "driver-class-name", ""));
        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;
    }

    /**
     * 注入事物管理器
     * 由于我们只使用一个事务管理器:DataSource1Config.DataSource2Config.java中配置的事务管理器了
     * @return
     */
    @Bean
    public JtaTransactionManager regTransactionManager () {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        UserTransaction userTransaction = new UserTransactionImp();
        return new JtaTransactionManager(userTransaction, userTransactionManager);
    }


    /**
     * 配置Druid监控
     * 1. 配置一个管理后台的Servlet
     * 2. 配置一个监控的filter
     */
    @Bean // 1. 配置一个管理后台的Servlet
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        //StatViewServlet是 配置管理后台的servlet
        ServletRegistrationBean<StatViewServlet> bean =
                new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        //配置初始化参数
        Map<String, String> initParam = new HashMap<>();
        //访问的用户名密码
        initParam.put(StatViewServlet.PARAM_NAME_USERNAME, "root");
        initParam.put(StatViewServlet.PARAM_NAME_PASSWORD, "123");
        //允许访问的ip,默认所有ip访问
        initParam.put(StatViewServlet.PARAM_NAME_ALLOW, "");
        //禁止访问的ip
        initParam.put(StatViewServlet.PARAM_NAME_DENY, "192.168.10.1");
        //监控配置界面中 是否能够重置数据(点了重置所有监控数据就没有了)
        //initParam.put(StatViewServlet.PARAM_NAME_RESET_ENABLE,"false");
        initParam.put(StatViewServlet.PARAM_NAME_RESET_ENABLE,"true");
        bean.setInitParameters(initParam);
        return bean;
    }
    //2. 配置一个监控的filter
    @Bean
    public FilterRegistrationBean<Filter> filter() {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new WebStatFilter());
        //配置初始化参数
        Map<String, String> initParam = new HashMap<>();
        //排除请求
        initParam.put(WebStatFilter.PARAM_NAME_EXCLUSIONS, "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*");
        bean.setInitParameters(initParam);
        //拦截所有请求
        bean.setUrlPatterns(Arrays.asList("/*"));
        return bean;
    }
}

修改application.yml

server:
  port: 8001

spring:
  mvc:
    date-format: yyyy-MM-dd HH:mm:ss #修改表单提交到后台的日期格式

  #数据源配置
  datasource:
    db1:
      username: root
      password: 12345678
      url: jdbc:mysql://192.168.5.130:3306/db1
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 8 #数据库连接池初始化连接大小
      minIdle: 5 #数据库连接池最小连接池数量
      maxActive: 20 #数据库连接池最大连接池数量
      maxWait: 60000 #获取连接时最大等待时间,单位毫秒
      timeBetweenEvictionRunsMillis: 60000 #有两个含义:1) Destroy线程会检测连接的间隔时间,即配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 2) testWhileIdle的判断依据;
      minEvictableIdleTimeMillis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒
      validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
      testWhileIdle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      testOnBorrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      poolPreparedStatements: false #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,分库分表较多的数据库,建议配置为false。
      maxPoolPreparedStatementPerConnectionSize: -1 #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      #监控配置
      filters: stat,wall,slf4j
      useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据,多数据源
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #druid.stat.mergeSql=true 合并执行的相同sql,避免因为参数不同而统计多条sql语句, druid.stat.slowSqlMillis=500 用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢

    db2:
      username: root
      password: 12345678
      url: jdbc:mysql://192.168.5.130:3306/db2
      driver-class-name: com.mysql.jdbc.Driver
      initialSize: 18 #数据库连接池初始化连接大小
      minIdle: 15 #数据库连接池最小连接池数量
      maxActive: 120 #数据库连接池最大连接池数量
      maxWait: 60000 #获取连接时最大等待时间,单位毫秒
      timeBetweenEvictionRunsMillis: 60000 #有两个含义:1) Destroy线程会检测连接的间隔时间,即配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 2) testWhileIdle的判断依据;
      minEvictableIdleTimeMillis: 300000 #配置一个连接在池中最小生存的时间,单位是毫秒
      validationQuery: SELECT 1 FROM DUAL #用来检测连接是否有效的sql
      testWhileIdle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
      testOnBorrow: false #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
      testOnReturn: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
      poolPreparedStatements: false #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭,分库分表较多的数据库,建议配置为false。
      maxPoolPreparedStatementPerConnectionSize: -1 #要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
      #监控配置
      filters: stat,wall,slf4j
      useGlobalDataSourceStat: true #合并多个DruidDataSource的监控数据,多数据源
      connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500 #druid.stat.mergeSql=true 合并执行的相同sql,避免因为参数不同而统计多条sql语句, druid.stat.slowSqlMillis=500 用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢

#mybatis配置文件
mybatis:
  #mapper-locations: classpath:mybatis/mapper/db1/*.xml #mybatis的sql的mapper接口映射文件(单数据源配置使用) 多数据源在对应的配置类中配置使用,这里配置不起作用了(比如DataSource1Config类中)
  #type-aliases-package: com.sgz.atomikos.**.entity #对应实体类的路径(可以不用配置)
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名转换

#pagehelper分页插件
pagehelper:
  helperDialect: mysql
  reasonable: true  #pageHelper里面自带的一个功能,叫做reasonable分页参数合理化,3.3.0以上版本可用,默认是false。 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages(超过总数时)会查询最后一页;禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据
  supportMethodsArguments: true
  params: count=countSql;

#日志配置
logging:
  level:
    root: info
    com.sgz.atomikos: debug
  file: D:/logs/springboot.log


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值