当前位置: 首页>后端>正文

SpringBoot整合mybatisPlus及分布式事务实现

一.整合mybatisPlus操作数据库

1.1 MyBatis-Plus简介

MyBatis-Plus(简称 MP)是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

  • 官网文档地址: https://mybatis.plus/guide/

1.2SpringBoot集成MybatisPlus

1.通过maven坐标将mybatis-plus-boot-starter以及数据库驱动引入到Spring Boot项目里面来。

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

2.保证application.yml里面有数据库连接的配置。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

3.配置Mybatis的Mapper类文件的包扫描路径

@MapperScan(basePackages = {"cn.lsp.springboot.mapper"})
@SpringBootApplication
public class SpringBootMybatisPlusApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootMybatisPlusApplication.class, args);
    }
}

1.3Mapper继承实现

如果我们操作数据库中的article表,我们需要按照article表的结构创建一个实体类。

@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Article {

    // id随数据库自增
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    private String author;

    private String title;

    private String content;

    private Date createTime;
}

然后写一个接口ArticleMapper ,继承自BaseMapper,泛型是Article实体类。

public interface ArticleMapper extends BaseMapper<Article> {

}

BaseMapper中默认帮我们提供了若干的增删改查基础实现,由于ArticleMapper 继承自BaseMapper,所以ArticleMapper 可以使用这些方法去操作数据库的article表。

1.4通过Mapper实现增删查改

@Slf4j
@Service
public class ArticleServiceImpl implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Override
    public void saveArticle(Article article) {
        articleMapper.insert(article);
    }

    @Override
    public void deleteArticle(Long id){
        articleMapper.deleteById(id);
    }

    @Override
    public void updateArticle(Article article){
        articleMapper.updateById(article);
    }

    @Override
    public Article getArticle(Long id){
        return articleMapper.selectById(id);
    }
}

1.5测试MybatisPlus

@Slf4j
@SpringBootTest
public class MybatisPlusTest {

    @Resource
    private ArticleService articleService;

    @Test
    public void testSaveArticle() {
        Article article = new Article();
        article.setTitle("SpringBoot实战");
        article.setContent("详细介绍SpringBoot的各种姿势");
        article.setAuthor("William");
        article.setCreateTime(new Date());
        articleService.saveArticle(article);

        log.info("articleId={}", article.getId());
    }

    @Test
    public void testGet() {
        Long id = 1L;
        Article article = articleService.getArticle(id);
        log.info(article.toString());
    }
}

二.mybatisplus+atomikos实现分布式事务

2.1 Mybatis plus多数据源以及分布式事务实现方式

  • 方案一:采用Mybatis Plus官网上实现的基于AOP以及注解的动态数据源切换方案。基于AOP以及注解的动态数据源切换方案。这个方案的优点是:数据源灵活切换。但缺点也同样明显:

    • 需要为每一个类或者持久层方法指定数据源,如果编码人员素质一般,很容易错误的使用数据源。
    • 动态切换数据源,也就意味着“从使用的角度”出错的概率变大。从而导致错误的配置使用分布式事务。版本兼容问题有可能此起彼伏。
  • 方案二:我们仍然采用最简的实现方式。就是将不同的数据库操作Mapper分包存放,分包注入使用不同的数据源。这种方式实现逻辑简单,万变不离其宗,是“约定大于配置”思想的体现,约定好了该放哪就放哪。虽然不灵活,但是使用方便,也不容易出错。即使出错,也容易发现(在package层面发现问题,比到代码里面去找Bug要容易的多)。
    本文主要讲解方案二的实现方式。

2.2 整合jta-atomikos

1.增加相关依赖mybatis-plus、jta-atomikos。

<dependency>
   <groupId>com.baomidou</groupId>
   <artifactId>mybatis-plus-boot-starter</artifactId>
   <version>3.3.2</version>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jta-atomikos</artifactId>
</dependency>

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
</dependency>

2.多数据源配置
两个数据源的名称分别是:primary和secondary。分别访问testdb和testdb2数据库,驱动类是MysqlXADataSource(支持分布式事务)

primarydb:
  uniqueResourceName: primary
  xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
  xaProperties:
    url: jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf-8&useSSL=false
    user: root
    password: root
  exclusiveConnectionMode: true
  minPoolSize: 3
  maxPoolSize: 10
  testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。

secondarydb:
  uniqueResourceName: secondary
  xaDataSourceClassName: com.mysql.cj.jdbc.MysqlXADataSource
  xaProperties:
    url: jdbc:mysql://localhost:3306/testdb2?useUnicode=true&characterEncoding=utf-8&useSSL=false
    user: root
    password: root
  exclusiveConnectionMode: true
  minPoolSize: 3
  maxPoolSize: 10
  testQuery: SELECT 1 from dual #由于采用HikiriCP,用于检测数据库连接是否存活。

3.创建XXXMapper类、实体类、和XXXMapper.xml文件


SpringBoot整合mybatisPlus及分布式事务实现,第1张
文件结构

2.2配置多数据源

数据源DataSource、SqlSessionFactory、SqlSessionTemplate、扫描路径,对于primarydb和secondarydb都是自己一套,需要分别配置。
数据源一:primarydb

@Configuration
//数据源primary-testdb库接口存放目录
@MapperScan(basePackages = "cn.lsp.springboot.mapper.testdb",
            sqlSessionTemplateRef = "primarySqlSessionTemplate")
public class PrimaryDataSourceConfig {

  @Bean(name = "primaryDataSource")
  @ConfigurationProperties(prefix = "primarydb")   //数据源primary配置
  @Primary
  public DataSource primaryDataSource() {
    return new AtomikosDataSourceBean();
  }

  @Bean(name = "primarySqlSessionFactory")
  @Primary
  public SqlSessionFactory primarySqlSessionFactory(
          @Qualifier("primaryDataSource") DataSource dataSource)
          throws Exception {
    MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    //设置XML文件存放位置
    bean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("classpath:mapper/testdb/*.xml")); //注意这里testdb目录
    return bean.getObject();
  }

  @Bean(name = "primarySqlSessionTemplate")
  @Primary
  public SqlSessionTemplate primarySqlSessionTemplate(
          @Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory)
          throws Exception {
    return new SqlSessionTemplate(sqlSessionFactory);
  }
}

数据源二:secondarydb。

@Configuration
@MapperScan(basePackages = "cn.lsp.springboot.mapper.testdb2",     //注意这里testdb2目录
            sqlSessionTemplateRef = "secondarySqlSessionTemplate")
public class SecondaryDataSourceConfig {

    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "secondarydb")    //注意这里secondary配置
    public DataSource secondaryDataSource() {
        return new AtomikosDataSourceBean();
    }

    @Bean(name = "secondarySqlSessionFactory")
    public SqlSessionFactory secondarySqlSessionFactory(
                        @Qualifier("secondaryDataSource") DataSource dataSource)
                        throws Exception {
        MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        //设置XML文件存放位置
        bean.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/testdb2/*.xml")); //注意这里testdb2目录
        return bean.getObject();
    }

    @Bean(name = "secondarySqlSessionTemplate")
    public SqlSessionTemplate secondarySqlSessionTemplate(
                        @Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory)
                        throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}

2.3统一事务管理器

虽然我们将数据源及其相关配置分成了两组,但这两组数据源使用的事务管理器必须是同一个,这样才能实现分布式事务。下面是事务管理器的配置。固定代码,不用修改。

@Configuration
@EnableTransactionManagement
public class XATransactionManagerConfig {

     //User事务
    @Bean(name = "userTransaction")
    public UserTransaction userTransaction() throws Throwable {
        UserTransactionImp userTransactionImp = new UserTransactionImp();
        userTransactionImp.setTransactionTimeout(10000);
        return userTransactionImp;
    }
    //分布式事务
    @Bean(name = "atomikosTransactionManager", initMethod = "init", destroyMethod = "close")
    public TransactionManager atomikosTransactionManager() throws Throwable {
        UserTransactionManager userTransactionManager = new UserTransactionManager();
        userTransactionManager.setForceShutdown(false);
        return userTransactionManager;
    }
    //事务管理器
    @Bean(name = "transactionManager")
    @DependsOn({ "userTransaction", "atomikosTransactionManager" })
    public PlatformTransactionManager transactionManager() throws Throwable {
        return new JtaTransactionManager(userTransaction(),atomikosTransactionManager());
    }
}

2.3多数据源及分布式事务测试

1.service类,在1.4节的ArticleService上增加了saveArticleAndComment方法和CommentMapper注入

@Slf4j
@Service
public class ArticleServiceImpl implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private CommentMapper commentMapper;

    @Override
    public void saveArticle(Article article) {
        articleMapper.insert(article);
    }

    @Override
    @Transactional
    public Long saveArticleAndComment(Article article, Comment comment) {
        articleMapper.insert(article);
        comment.setArticleId(article.getId());
        commentMapper.insert(comment);
//        int a = 2 / 0;
        return article.getId();
    }

    @Override
    public void deleteArticle(Long id){
        articleMapper.deleteById(id);
    }

    @Override
    public void updateArticle(Article article){
        articleMapper.updateById(article);
    }

    @Override
    public Article getArticle(Long id){
        return articleMapper.selectById(id);
    }
}

2.测试类

@Slf4j
@SpringBootTest
public class MybatisPlusJtaTest {

    @Resource
    private ArticleService articleService;

    @Test
    public void testSaveArticleAndComment() {
        Article article = new Article();
        article.setTitle("SpringBoot实战");
        article.setContent("详细介绍SpringBoot的各种姿势");
        article.setAuthor("William");
        article.setCreateTime(new Date());

        Comment comment = new Comment();
        comment.setName("Tom");
        comment.setContent("内容详实,偏实战");
        comment.setCreateTime(new Date());

        articleService.saveArticleAndComment(article, comment);
    }
}

正常情况下,两组数据分别插入到testdb的article表和testdb2的comment表。如果我们人为制造一个异常(如上面代码),事务回滚,二者均无法插入数据。


https://www.xamrdz.com/backend/3by1941200.html

相关文章: