springboot 整合mybatis-plus+dynamic-datasource实现读写分离

博客介绍了Spring Boot整合MyBatis的步骤。首先导包,数据源用druid,可自选版本;接着写配置,数据库用SqlServer可自行修改,还需重写读写分离插件配置等;最后进行测试,未加@DS注解时查询用从库,增删改用主库,加注解则以注解为准。

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

第一步、导包

  数据源使用druid,这里druid和mybatis-plus版本不是最新,可自行选择。

	<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
	<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
	<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>2.1.9</version>
        </dependency>			

第二步、写配置

  这里数据库使用的SqlServer,自行修改,oracle或mysql见jdbc快速入坑(一)

  • application.yml配置如下:
# Tomcat
server:
    tomcat:
        uri-encoding: UTF-8
        max-threads: 1000
        min-spare-threads: 30
    port: 8088
spring:
    datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
        druid:
            stat-view-servlet:
                enabled: true
                url-pattern: /druid/*
                loginUsername: admin
                loginPassword: 123456
        dynamic:
            primary: master
            health: true
            druid:
                initial-size: 10
                max-active: 100
                min-idle: 10
                max-wait: 60000
                pool-prepared-statements: true
                max-pool-prepared-statement-per-connection-size: 20
                time-between-eviction-runs-millis: 60000
                min-evictable-idle-time-millis: 300000
                test-while-idle: true
                test-on-borrow: false
                test-on-return: false
                validationQuery: "select 1"
                filters: stat,wall
                stat:
                    log-slow-sql: true
                    slow-sql-millis: 1000
                    merge-sql: false
                wall:
                    multi-statement-allow: true
            datasource:
                master:  #数据源1
                    url: jdbc:sqlserver://127.0.0.1:1051;databaseName=test
                    username: ENC(L36gh72XwqNKz00VQnxz9XhApDcpf5Q4oaW5nwbpE4e1TBGL81HxQcfI64eBXMiSTwI8+f70hW50aX2iZdFyBw==)
                    password: ENC(f4j9JTNqd7XkuSlDLM1od6AByM8zw/crcd4lo47l97JIWD62VvBXJK5EFBWV67We4wThc9r0oIc8tKdfsZbO5g==)
                slave:
                    url: jdbc:sqlserver://121.196.190.248:1050;databaseName=test
                    username: ENC(L36gh72XwqNKz00VQnxz9XhApDcpf5Q4oaW5nwbpE4e1TBGL81HxQcfI64eBXMiSTwI8+f70hW50aX2iZdFyBw==)
                    password: ENC(f4j9JTNqd7XkuSlDLM1od6AByM8zw/crcd4lo4Ml97JIWD62VvBXJK5EFBWV67We4wThc9r0oIc8tKdfsZbO5g==)
mybatis-plus:
    mapper-locations: classpath*:mapper/**/*.xml
    #实体扫描,多个package用逗号或者分号分隔
    typeAliasesPackage: com.test.*.entity
    global-config:
        #主键类型  0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
        id-type: 0
        #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
        field-strategy: 1
        #驼峰下划线转换
        db-column-underline: true
        #刷新mapper 调试神器
        refresh-mapper: true
        #数据库大写下划线转换
        #capital-mode: true
        # Sequence序列接口实现类配置
        #key-generator: com.baomidou.mybatisplus.incrementer.OracleKeyGenerator
        #逻辑删除配置
        logic-delete-value: -1
        logic-not-delete-value: 0
        #自定义填充策略接口实现
        #meta-object-handler: com.baomidou.springboot.xxx
        #自定义SQL注入器
        sql-injector: com.baomidou.mybatisplus.mapper.LogicSqlInjector
    configuration:
        map-underscore-to-camel-case: true
        cache-enabled: false
        call-setters-on-nulls: true
        #打印mybatis的执行sql
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

  • 重写读写分离插件配置

import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.support.DbHealthIndicator;
import com.baomidou.dynamic.datasource.support.DdConstants;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import java.util.Properties;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;


@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
@Slf4j
public class MasterSlaveAutoRoutingPlugin implements Interceptor {

    @Autowired
    private DynamicDataSourceProperties properties;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] args = invocation.getArgs();
        MappedStatement ms = (MappedStatement) args[0];
        try {
            if (StringUtils.isEmpty(DynamicDataSourceContextHolder.peek())){
                String source = SqlCommandType.SELECT == ms.getSqlCommandType() ? DdConstants.SLAVE : DdConstants.MASTER;
                DynamicDataSourceContextHolder.push(source);
            }
            return invocation.proceed();
        } finally {
            DynamicDataSourceContextHolder.poll();
        }
    }

    /**
     * 获取动态数据源名称,重写注入 DbHealthIndicator 支持数据源健康状况判断选择
     *
     * @param mappedStatement mybatis MappedStatement
     * @return 获取真实的数据源名称
     */
    public String getDataSource(MappedStatement mappedStatement) {
        String slave = DdConstants.SLAVE;
        if (properties.isHealth()) {
            /*
             * 根据从库健康状况,判断是否切到主库
             */
            boolean health = DbHealthIndicator.getDbHealth(DdConstants.SLAVE);
            if (!health) {
                health = DbHealthIndicator.getDbHealth(DdConstants.MASTER);
                if (health) {
                    slave = DdConstants.MASTER;
                }
            }
        }
        return SqlCommandType.SELECT == mappedStatement.getSqlCommandType() ? slave : DdConstants.MASTER;
    }

    @Override
    public Object plugin(Object target) {
        return target instanceof Executor ? Plugin.wrap(target, this) : target;
    }

    @Override
    public void setProperties(Properties properties) {
    }
}
  • 新建MybaitsPlus配置类MybatisPlusConfig
import com.baomidou.mybatisplus.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        return new PaginationInterceptor();
    }

    /**
     * 读写分离插件
     */
    @Bean
    public MasterSlaveAutoRoutingPlugin masterSlaveAutoRoutingPlugin(){
        return new MasterSlaveAutoRoutingPlugin();
    }

}

  • 定义数据源常量
public interface DBConstants {
   String MASTER = "master";
   String SLAVE_GROUP = "slave";
   String SLAVE1 = "slave_1";
}

第三步、测试

  • 测试代码

@RequestMapping("/test")
@RestController
public class TestController {

    @Autowired
    private AreaService areaService;

    /**
     * 使用主库查询
     */
    @GetMapping
    @DS(DBConstants.MASTER)
    public Object test(){
        Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
        return areaService.selectList(wrapper);
    }

    /**
     * 未加@DS注解,查询操作默认使用slave从库,插入操作默认使用master主库 
     */
    @GetMapping("/11")
    public Object test11(){
        Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
        return areaService.selectList(wrapper);
    }

    /**
     *使用从库查询 
     */
    @GetMapping("/22")
    @DS(DBConstants.SLAVE_GROUP)
    public Object test22(){
        Wrapper<AreaEntity> wrapper = new EntityWrapper<>();
        return areaService.selectList(wrapper);
    }
}

  当未加@DS注解时,查询操作默认使用从库,修改删除和添加操作默认使用主库,如果添加@DS注解,则以注解为准。

<think>好的,我现在需要帮助用户解决如何使用Spring Boot、MyBatis-Plusdynamic-datasource-spring-boot-starter来构建一个包含分库分表和动态数据源的Demo项目。首先,我应该回顾用户提供的引用内容,看看里面有没有相关的信息。 用户提供的引用中提到了dynamic-datasource-spring-boot-starter可以支持多数据源,适用于多种场景,包括纯粹多库、读写分离等。引用3和5提到了整合MyBatis-Plusdynamic-datasource的依赖配置,还有示例的代码结构。不过,分库分表的具体实现可能需要更多的信息,比如如何路由数据到不同的库和表。 接下来,我需要考虑分库分表的常见实现方式。通常,分库分表可以通过中间件如ShardingSphere,或者通过应用层自己实现路由逻辑。而dynamic-datasource-spring-boot-starter主要处理动态数据源切换,可能结合分库分表策略需要在代码中自定义路由。 用户需要示例代码,所以需要给出具体的步骤:依赖配置、数据源配置、分库分表策略、动态数据源切换方式、MyBatis-Plus的集成以及业务层的使用示例。 首先,确认依赖是否正确。根据引用5,dynamic-datasource的版本是3.4.1,MyBatis-Plus是3.1.2。不过可能需要使用更新的版本,但用户示例可能沿用这些版本。 然后,配置文件中需要设置多个数据源,比如master、slave1、slave2等,每个对应不同的数据库。分库分表的话,可能需要根据某个字段(比如用户ID)的哈希值或者取模来决定使用哪个数据源和表名。 动态数据源的核心是AbstractRoutingDataSource,而dynamic-datasource已经封装了这部分,可以通过@DS注解来切换数据源。分表的话,可能需要在SQL中动态替换表名,比如使用MyBatis的拦截器或者自定义注解。 在编写示例代码时,需要演示如何在Service层使用@DS注解切换数据源,以及如何根据分片键计算数据源和表名。例如,根据用户ID的奇偶性选择不同的数据源和表后缀。 可能还需要注意事务管理的问题,确保在切换数据源时事务能正确处理。此外,分库分表后的主键生成策略也需要考虑,比如使用分布式ID生成器。 最后,生成相关问题,考虑用户可能遇到的性能优化、事务处理、动态添加数据源等问题。</think>以下是一个基于Spring Boot、MyBatis-Plusdynamic-datasource-spring-boot-starter实现分库分表与动态数据源的示例指南: --- ### **1. 环境准备** **依赖配置**(pom.xml): ```xml <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> ``` --- ### **2. 数据源配置** **application.yml**: ```yaml spring: datasource: dynamic: primary: master # 默认数据源 strict: false # 允许未匹配到数据源时回退到默认 datasource: master: url: jdbc:mysql://localhost:3306/db0?useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver slave1: url: jdbc:mysql://localhost:3306/db1?useSSL=false username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver ``` --- ### **3. 分库分表策略** #### **分库逻辑**(按用户ID分库): ```java public class ShardingContext { private static final ThreadLocal<String> DB_KEY = new ThreadLocal<>(); public static void setDbKey(String dbKey) { DB_KEY.set(dbKey); } public static String getDbKey() { return DB_KEY.get(); } public static void clear() { DB_KEY.remove(); } } // 在AOP中根据分片键计算数据源 @Aspect @Component public class DataSourceAspect { @Before("@annotation(ds)") public void before(DS ds) { Long userId = getCurrentUserId(); // 实际业务中获取分片键 String dbKey = "slave" + (userId % 2); ShardingContext.setDbKey(dbKey); } } ``` #### **分表逻辑**(按用户ID分表): ```java public class UserTableSharding { public static String getTableSuffix(Long userId) { return "_" + (userId % 4); // 分为4张表:user_0 ~ user_3 } } ``` --- ### **4. 动态数据源切换** **Service层使用@DS注解**: ```java @Service @DS("#dbKey") // 动态占位符从ShardingContext获取 public class UserServiceImpl extends ServiceImpl<UserMapper, User> { public User getUserById(Long userId) { // 分表查询 String tableName = "user" + UserTableSharding.getTableSuffix(userId); return baseMapper.selectByTable(userId, tableName); } } // Mapper接口 public interface UserMapper extends BaseMapper<User> { @Select("SELECT * FROM ${tableName} WHERE id = #{userId}") User selectByTable(@Param("userId") Long userId, @Param("tableName") String tableName); } ``` --- ### **5. 核心配置类** **启动类注解**: ```java @SpringBootApplication @MapperScan("com.example.mapper") public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` --- ### **6. 事务管理** 使用`@Transactional`时需指定事务管理器: ```java @Transactional(rollbackFor = Exception.class, transactionManager = "dynamicTransactionManager") public void saveUser(User user) { // 分库分表保存逻辑 } ``` --- ### **注意事项** 1. **分片键计算**:需保证分片键(如用户ID)的均匀分布 2. **分布式ID生成**:建议使用雪花算法(`MyBatis-Plus`内置`@TableId(type = IdType.ASSIGN_ID)`) 3. **跨库查询**:尽量避免跨库事务操作 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值