Springboot + mybatisPlus 的多数据源的事务

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: springboot开箱即用就不多说,mybatisplus强大的持久层插件,一键生成代码。这二者的结合可以使得开发效率大幅度提升。然而对于初学着,多数据源及多数据源下的事务的处理,可能让你头疼。

springboot开箱即用就不多说,mybatisplus强大的持久层插件,一键生成代码。这二者的结合可以使得开发效率大幅度提升。然而对于初学着,多数据源及多数据源下的事务的处理,可能让你头疼。本文着重就mybatis-plus的多数据源的事务问题给出一些参考性解决方案。

datasource.yml文件配置


spring:
    user_center:
      url: jdbc:mysql://192.168.1.2:3306/a?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=utf8
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
    center_user:
      url: jdbc:mysql://192.168.1.2:3306/b?rewriteBatchedStatements=true&useUnicode=true&characterEncoding=UTF-8
      username: root
      password: root
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      filters: stat
      maxActive: 20
      initialSize: 1
      maxWait: 60000
      minIdle: 1
      timeBetweenEvictionRunsMillis: 60000
      minEvictableIdleTimeMillis: 300000
      validationQuery: select 'x'
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      poolPreparedStatements: true
      maxOpenPreparedStatements: 20
AI 代码解读

application.yml配置


spring:
  application:
    name: auth-service
  profiles.active: dev
  aop:
   proxy-target-class: true
   auto: true

server:
  port: 18601

mybatis:
  type-aliases-package: com.br.auth.entity.*
  mapper-locations: classpath:mapper/*/*.xml
#logging
logging:
  level: warn


AI 代码解读

DataSourceConfiguration.java

package com.br.auth.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by jack-cooper on 2017/1/18.
 */
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration implements TransactionManagementConfigurer {

    private static final Logger log = LoggerFactory.getLogger(DataSourceConfiguration.class);

    @Value("${spring.center_user.type}")
    private Class<? extends DataSource> dataSourceType;

    @Bean(name = "centerUserDataSource", destroyMethod = "close", initMethod = "init")
    @Primary
    @ConfigurationProperties(prefix = "spring.center_user")
    public DataSource centerUserDataSourceOne() {
        log.info("-------------------- 服务平台-用户中心 init ---------------------");
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean(name = "userCenterDataSource", destroyMethod = "close", initMethod="init")
    @ConfigurationProperties(prefix = "spring.user_center")
    public DataSource userCenterDataSourceOne() {
        log.info("-------------------- 后台 init ---------------------");
        return DataSourceBuilder.create().type(dataSourceType).build();
    }

    @Bean("dataSources")
    public Map<String,DataSource> dataSources(){
        Map<String,DataSource> dataSources = new HashMap<>();
        dataSources.put(DataSourceType.center_user.getType(), centerUserDataSourceOne());
        dataSources.put(DataSourceType.user_center.getType(),userCenterDataSourceOne());
        return dataSources;
    }


    /**
     * 有多少个数据源就要配置多少个bean
     *
     * @return
     */
    @Bean(name = "routingDataSource")
    public MyAbstractRoutingDataSource roundRobinDataSouceProxy() {
        int size = Integer.parseInt("1");
        MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource(size);
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        targetDataSources.put(DataSourceType.center_user.getType(), dataSources());
        for (String type : dataSources().keySet()) {
            targetDataSources.put(type, dataSources().get(type));
        }
        proxy.setDefaultTargetDataSource(centerUserDataSourceOne());
        proxy.setTargetDataSources(targetDataSources);
        return proxy;
    }

    /**
     * 事务配置,考虑多数据源情况下
     * @return
     */
    @Bean
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(roundRobinDataSouceProxy());
    }

    @Override
    public PlatformTransactionManager annotationDrivenTransactionManager() {
        return txManager();
    }
}

AI 代码解读

DataSourceContextHolder.java

package com.br.auth.config;

/**
 * Created by jack-cooper on 2017/1/18.
 */
public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder  = new ThreadLocal<String>();

    /**
     * 使用setDataSourceType设置当前的
     * @param dataSourceType
     */
    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}

AI 代码解读

DataSourceType.java

package com.br.auth.config;


/**
 * Created by jack-cooper on 2017/1/18.
 */
public enum DataSourceType {
    user("user", "后台"),
    order("order", "服务平台-用户中心");

    private String type;
    private String name;

    DataSourceType(String type, String name) {
        this.type = type;
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

AI 代码解读

MyAbstractRoutingDataSource.java

package com.br.auth.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by jack-cooper on 2017/1/18.
 */
public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {

    private final int dataSourceNumber;

    private AtomicInteger count = new AtomicInteger(0);

    public MyAbstractRoutingDataSource(int dataSourceNumber) {
        this.dataSourceNumber = dataSourceNumber;
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSourceType();
    }
}

AI 代码解读

MybatisPlusConfig.java

package com.br.auth.config;

import com.baomidou.mybatisplus.MybatisConfiguration;
import com.baomidou.mybatisplus.MybatisXMLLanguageDriver;
import com.baomidou.mybatisplus.entity.GlobalConfiguration;
import com.baomidou.mybatisplus.enums.DBType;
import com.baomidou.mybatisplus.enums.FieldStrategy;
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import com.br.auth.util.PropertiesUtil;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * @author 10400
 * @create 2017-07-03 11:28
 */
@Configuration
@Import({DataSourceConfiguration.class})
@MapperScan("com.br.auth.mapper.*")
@EnableTransactionManagement
public class MybatisPlusConfig {
    @Autowired(required = false)
    private Interceptor[] interceptors;
    @Autowired
    private ResourceLoader resourceLoader = new DefaultResourceLoader();
    @Value("${spring.user.type}")
    private Class<? extends DataSource> dataSourceType;

    @Autowired(required = false)
    private MybatisProperties properties;
    @Autowired(required = false)
    private DatabaseIdProvider databaseIdProvider;
    @Autowired(required = false)
    private PropertiesBean propertiesBean;

    @Resource(name = "routingDataSource")
    private AbstractRoutingDataSource routingDataSource;
    /**
     * mybatis-plus分页插件<br>
     * 文档:http://mp.baomidou.com<br>
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * 这里全部使用mybatis-autoconfigure 已经自动加载的资源。不手动指定
     * 配置文件和mybatis-boot的配置文件同步
     *
     * @return
     */
    @Bean
    public MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean() throws Exception {
        PropertiesUtil.setProperties(propertiesBean.getPropertiesBean());
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(routingDataSource);
        mybatisPlus.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            mybatisPlus.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        mybatisPlus.setConfiguration(properties.getConfiguration());
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            mybatisPlus.setPlugins(this.interceptors);
        }
        // MP 全局配置,更多内容进入类看注释
        GlobalConfiguration globalConfig = new GlobalConfiguration();
        globalConfig.setDbType(DBType.MYSQL.name());
        // ID 策略 AUTO->`0`("数据库ID自增") INPUT->`1`(用户输入ID") ID_WORKER->`2`("全局唯一ID") UUID->`3`("全局唯一ID")
        globalConfig.setIdType(0);
        globalConfig.setFieldStrategy(FieldStrategy.NOT_EMPTY.getKey());
        mybatisPlus.setGlobalConfig(globalConfig);
        MybatisConfiguration mc = new MybatisConfiguration();
        mc.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);
        //mapUnderscoreToCamelCase:是否启用下划线与驼峰式命名规则的映射(如first_name => firstName)
        mc.setMapUnderscoreToCamelCase(false);
        mybatisPlus.setConfiguration(mc);
        if (this.databaseIdProvider != null) {
            mybatisPlus.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            mybatisPlus.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            mybatisPlus.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            mybatisPlus.setMapperLocations(this.properties.resolveMapperLocations());
        }
        return mybatisPlus;
    }
}

AI 代码解读

DataSourceAop.java

package com.br.auth.config;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;



/**
 * desc: 数据源切面配置
 * Created by jack-cooper on 2017/1/18.
 */
@Aspect
@Order(-1)
@Component
public class DataSourceAop {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Before("execution(* com.br.auth.service.centerUser..*.*(..))")
    public void setCenterUserDataSourceType() throws Exception {
        DataSourceContextHolder.setDataSourceType(DataSourceType.center_user.getType());
        log.info("dataSource == >: centerUser");
    }

    @Before("execution(* com.br.auth.service.userCenter..*.*(..))")
    public void setUserCenterDataSourceType() throws Throwable{
        DataSourceContextHolder.setDataSourceType(DataSourceType.user_center.getType());
        log.info("dataSource == >:userCenter");
    }


    @After("execution(* com.br.auth.service.*..*.*(..)) ")
    public void afterReturning() throws Throwable {
        DataSourceContextHolder.clearDataSourceType();
        log.info("=====> clear dataSource aop ");
    }
}

AI 代码解读

文章参考:
http://www.cnblogs.com/sweetchildomine/p/6977987.html
http://www.cnblogs.com/softidea/p/7127874.html

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
20
分享
相关文章
Spring Boot 3 整合 Mybatis-Plus 实现数据权限控制
本文介绍了如何在Spring Boot 3中整合MyBatis-Plus实现数据权限控制,通过使用MyBatis-Plus提供的`DataPermissionInterceptor`插件,在不破坏原有代码结构的基础上实现了细粒度的数据访问控制。文中详细描述了自定义注解`DataScope`的使用方法、`DataPermissionHandler`的具体实现逻辑,以及根据用户的不同角色和部门动态添加SQL片段来限制查询结果。此外,还展示了基于Spring Boot 3和Vue 3构建的前后端分离快速开发框架的实际应用案例,包括项目的核心功能模块如用户管理、角色管理等,并提供Gitee上的开源仓库
286 11
SpringBoot配置多数据源实战
第四届光学与机器视觉国际学术会议(ICOMV 2025) 2025 4th International Conference on Optics and Machine Vision
87 8
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
962 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
springBoot:后端解决跨域&Mybatis-Plus&SwaggerUI&代码生成器 (四)
本文介绍了后端解决跨域问题的方法及Mybatis-Plus的配置与使用。首先通过创建`CorsConfig`类并设置相关参数来实现跨域请求处理。接着,详细描述了如何引入Mybatis-Plus插件,包括配置`MybatisPlusConfig`类、定义Mapper接口以及Service层。此外,还展示了如何配置分页查询功能,并引入SwaggerUI进行API文档生成。最后,提供了代码生成器的配置示例,帮助快速生成项目所需的基础代码。
364 1
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
该文档详细介绍了如何在Springboot Web项目中整合Mybatis,包括添加依赖、使用`@MapperScan`注解配置包扫描路径等步骤。若未使用`@MapperScan`,系统会自动扫描加了`@Mapper`注解的接口;若使用了`@MapperScan`,则按指定路径扫描。文档还深入分析了相关源码,解释了不同情况下的扫描逻辑与优先级,帮助理解Mybatis在Springboot项目中的自动配置机制。
266 0
Springboot整合Mybatis,MybatisPlus源码分析,自动装配实现包扫描源码
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
springboot整合mybatis-plus及mybatis-plus分页插件的使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis-Plus及其分页插件,包括依赖引入、配置文件编写、SQL表创建、Mapper层、Service层、Controller层的创建,以及分页插件的使用和数据展示HTML页面的编写。
springboot整合mybatis-plus及mybatis-plus分页插件的使用
技术分享:使用Spring Boot3.3与MyBatis-Plus联合实现多层次树结构的异步加载策略
在现代Web开发中,处理多层次树形结构数据是一项常见且重要的任务。这些结构广泛应用于分类管理、组织结构、权限管理等场景。为了提升用户体验和系统性能,采用异步加载策略来动态加载树形结构的各个层级变得尤为重要。本文将详细介绍如何使用Spring Boot3.3与MyBatis-Plus联合实现这一功能。
185 2
SpringBoot 3.3.2 + ShardingSphere 5.5 + Mybatis-plus:轻松搞定数据加解密,支持字段级!
【8月更文挑战第30天】在数据驱动的时代,数据的安全性显得尤为重要。特别是在涉及用户隐私或敏感信息的应用中,如何确保数据在存储和传输过程中的安全性成为了开发者必须面对的问题。今天,我们将围绕SpringBoot 3.3.2、ShardingSphere 5.5以及Mybatis-plus的组合,探讨如何轻松实现数据的字段级加解密,为数据安全保驾护航。
496 1
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等