使用 Mybatis-Plus 实现 MySQL 数据库读写分离需要进行如下步骤:

1.添加Mybatis-Plus的依赖

在 pom.xml 文件中添加 Mybatis-Plus 的依赖,例如:

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

2.配置数据源

在 Spring Boot 项目的配置文件 application.properties 或 application.yml 中,配置主数据库和从数据库的数据源信息,例如:

# 主数据库
spring.datasource.master.url = jdbc:mysql://localhost:3306/db_master?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.master.username = root
spring.datasource.master.password = 123456
spring.datasource.master.driver-class-name = com.mysql.cj.jdbc.Driver

# 从数据库
spring.datasource.slave.url = jdbc:mysql://localhost:3306/db_slave?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
spring.datasource.slave.username = root
spring.datasource.slave.password = 123456
spring.datasource.slave.driver-class-name = com.mysql.cj.jdbc.Driver

3.配置 Mybatis-Plus

在 Spring Boot 项目的配置文件中,配置 Mybatis-Plus 的相关选项,例如:

mybatis-plus:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.example.domain
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4.配置读写分离

在 Spring Boot 项目的配置文件中,可以通过配置 Mybatis-Plus 的 AbstractRoutingDataSource 实现读写分离,例如:

@Configuration
public class DataSourceConfig {
    @Bean(name = "master")
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "slave")
    @ConfigurationProperties("spring.datasource.slave")
    public DataSource slaveDataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "dynamicDataSource")
    public AbstractRoutingDataSource dynamicDataSource(@Qualifier("master") DataSource masterDataSource,
                                                       @Qualifier("slave") DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DatasourceType.MASTER.name(), masterDataSource);
        targetDataSources.put(DatasourceType.SLAVE.name(), slaveDataSource);

        DynamicDataSource dataSource = new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(masterDataSource);
        return dataSource;
    }
}

其中,DynamicDataSource 继承 AbstractRoutingDataSource 并实现了 determineCurrentLookupKey 方法,根据需要路由到主数据库或从数据库。

5.实现多数据源

创建一个 DatasourceType 的枚举类,定义主从数据源的类型,例如:

public enum DatasourceType {
    MASTER,
    SLAVE
}

创建一个 DynamicDataSource 类,继承 AbstractRoutingDataSource,并实现 determineCurrentLookupKey 方法,根据需要路由到主数据库或从数据库;同时,在方法中实现数据源的切换:

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DatasourceContextHolder.getDatasourceType();
    }

    @Override
    public Connection getConnection() throws SQLException {
        String dsKey = (String) determineCurrentLookupKey();
        if (dsKey != null && !dsKey.isEmpty()) {
            DataSource dataSource = determineTargetDataSource();
            if (dataSource == null) {
                throw new IllegalStateException("数据源不存在:" + dsKey);
            }
            return dataSource.getConnection();
        } else {
            return super.getConnection();
        }
    }
}

6.实现数据源的动态切换

在执行数据库操作前,将数据源类型存储在 ThreadLocal 变量中,并在数据源有变更时更新该变量:

public class DatasourceContextHolder {
    private static final ThreadLocal<String> DATASOURCE_TYPE = ThreadLocal.withInitial(() -> DatasourceType.MASTER.name());

    public static String getDatasourceType() {
        return DATASOURCE_TYPE.get();
    }

    public static void setDatasourceType(String datasourceType) {
        DATASOURCE_TYPE.set(datasourceType);
    }

    public static void clearDatasourceType() {
        DATASOURCE_TYPE.remove();
    }
}

7.实现 Mybatis-Plus 的拦截器

Mybatis-Plus 支持自定义拦截器,可以在拦截器中动态地设置数据源类型。在拦截器中实现 Intercept 方法,并在其中设置要使用的数据源类型:

public class DynamicDataSourceInterceptor implements Interceptor {
    private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) invocation.getSignature();
        Class<?> mapperClass = methodSignature.getDeclaringType();
        Method method = methodSignature.getMethod();

        DataSource dataSourceAnnotation = AnnotationUtils.findAnnotation(mapperClass, DataSource.class);
        if (dataSourceAnnotation == null) {
            dataSourceAnnotation = AnnotationUtils.findAnnotation(method, DataSource.class);
        }

        if (dataSourceAnnotation != null) {
            DatasourceContextHolder.setDatasourceType(dataSourceAnnotation.value().name());
            logger.debug("设置数据源类型:{}", dataSourceAnnotation.value());
        } else {
            logger.debug("未设置数据源类型,使用默认数据源:{}", DatasourceType.MASTER);
        }

        try {
            return invocation.proceed();
        } finally {
            DatasourceContextHolder.clearDatasourceType();
        }
    }

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

    @Override
    public void setProperties(Properties properties) {
    }
}

其中,DataSource 注解用于指定要使用的数据源类型,例如:

@DataSource(DatasourceType.SLAVE)
public interface UserMapper extends BaseMapper<User> {
    // ...
}

最后,在 Mybatis-Plus 的配置文件中,注册拦截器:

@Configuration
public class MybatisPlusConfig {
    @Bean
    public Interceptor dynamicDataSourceInterceptor() {
        return new DynamicDataSourceInterceptor();
    }

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(Interceptor[] interceptors) {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.setInterceptors(interceptors);
        return mybatisPlusInterceptor;
    }
}

通过以上步骤,就可以在 Spring Boot 项目中使用 Mybatis-Plus 实现 MySQL 数据库的读写分离了。