[技术资料]Spring Boot 整合 MyBatis 实现多数据源的整合与配置

在实际的应用开发中,往往会遇到需要从多个数据库中获取数据的场景。MyBatis 提供了灵活的方式来整合和配置多数据源,适应不同的数据存储需求。本文将详细探讨 MyBatis 如何整合多数据源,并通过两种常见的方式(分包配置与 AOP 动态切换)进行实现。

为什么需要多数据源?

场景 1:不同数据库

在某些复杂系统中,可能会使用不同类型的数据库来存储不同的数据。例如,一个商城网站和一个游戏网站,它们可能需要共享一些公共数据,但由于各自的业务特性,它们分别使用不同的数据库。商城网站涉及大量的交易数据,需要保证安全性;而游戏网站可能流量更大,可能会选择不同的数据库来提升性能或应对大规模并发。

通过使用多数据源,我们可以把不同的数据库隔离开来,降低一个系统故障对另一个系统的影响,同时提高性能与安全性。

场景 2:同一数据库,分别访问

随着业务的发展,单一数据库可能无法满足性能要求。此时,为了提高性能或扩展性,可以考虑对数据库进行拆分,使用多个数据源进行轮询访问。例如,当数据库中的数据量大到一定程度时,可能需要对数据进行分库分表,以减轻单个数据库的负担,提升查询性能。

多数据源的配置方法

MyBatis 提供了多种方式来整合多个数据源,主要包括 分包配置AOP 动态切换 两种方式。

1. 分包配置(场景 1)

分包配置的核心思想是为每个数据源配置不同的 SqlSessionFactorySqlSessionTemplate,分别处理不同的数据源请求。以下是具体的配置步骤:

1.1 配置数据源

首先,我们在 application.ymlapplication.properties 文件中配置多个数据源。例如,配置两个数据源 boot1boot2

# 数据源 1
spring.datasource.one.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.one.username=root
spring.datasource.one.password=root
spring.datasource.one.jdbc-url=jdbc:mysql://localhost:3306/boot1?characterEncoding=utf8&serverTimezone=GMT%2B8

# 数据源 2
spring.datasource.two.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.two.username=root
spring.datasource.two.password=root
spring.datasource.two.jdbc-url=jdbc:mysql://localhost:3306/boot2?characterEncoding=utf8&serverTimezone=GMT%2B8
1.2 配置 DataSource

接下来,我们需要通过 Java 配置类定义这两个数据源。使用 @ConfigurationProperties 注解来加载配置,并提供两个不同的 DataSource Bean。

@Configuration
public class DataSourceConfig {

    @Primary
    @Bean("dsOne")
    @ConfigurationProperties(prefix = "spring.datasource.one")
    public DataSource dsOne() {
        return DataSourceBuilder.create().build();
    }

    @Bean("dsTwo")
    @ConfigurationProperties(prefix = "spring.datasource.two")
    public DataSource dsTwo() {
        return DataSourceBuilder.create().build();
    }
}
1.3 配置 SqlSessionFactory 和 SqlSessionTemplate

然后,我们为每个数据源分别配置 SqlSessionFactorySqlSessionTemplate,并指定每个数据源所使用的 Mapper 包。

@Configuration
@MapperScan(basePackages = "com.example.springbootpro1.mapper", sqlSessionTemplateRef = "sqlSessionTemplate1")
public class MyBatisConfigOne {

    @Resource(name = "dsOne")
    private DataSource dsOne;

    @Bean("sqlSessionFactory1")
    public SqlSessionFactory sqlSessionFactory1() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dsOne);
        factoryBean.setTypeAliasesPackage("com.example.springbootpro1.entity");
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate1() {
        return new SqlSessionTemplate(sqlSessionFactory1());
    }
}
1.4 配置第二个数据源的 SqlSessionFactorySqlSessionTemplate

同样的,我们也为第二个数据源配置 SqlSessionFactorySqlSessionTemplate

@Configuration
@MapperScan(basePackages = "com.example.springbootpro1.dao", sqlSessionTemplateRef = "sqlSessionTemplate2")
public class MyBatisConfigTwo {

    @Resource(name = "dsTwo")
    private DataSource dsTwo;

    @Bean("sqlSessionFactory2")
    public SqlSessionFactory sqlSessionFactory2() throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dsTwo);
        factoryBean.setTypeAliasesPackage("com.example.springbootpro1.bean");
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
        return factoryBean.getObject();
    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate2() {
        return new SqlSessionTemplate(sqlSessionFactory2());
    }
}

2. AOP 动态切换数据源(场景 2)

AOP 动态切换数据源的核心思想是通过 AOP 拦截方法调用,根据注解或上下文决定使用哪个数据源。实现的关键是使用 AbstractRoutingDataSource 来动态决定当前使用的数据源。

2.1 配置动态数据源

首先,我们配置多个数据源,并使用 AbstractRoutingDataSource 来实现数据源的动态切换。

@Configuration
@EnableTransactionManagement
public class DataSourceConfig {

    @Primary
    @Bean(name = "master")
    @ConfigurationProperties(prefix = "spring.datasource.one")
    public DataSource master() {
        return DataSourceBuilder.create().build();
    }

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

    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map<Object, Object> dataSourceMap = new HashMap<>(2);
        dataSourceMap.put("master", master());
        dataSourceMap.put("slave", slave());
        dynamicDataSource.setDefaultTargetDataSource(master());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }
}

2.2 实现数据源上下文

为了在方法调用时动态决定数据源,我们需要一个上下文类来存储当前的数据源。

public class DynamicDataSourceContextHolder {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
        @Override
        protected String initialValue() {
            return "master";
        }
    };

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

    public static void setDataSourceKey(String key) {
        contextHolder.set(key);
    }

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

2.3 实现 AOP 切面

通过 AOP 切面拦截标有 @DataSource 注解的方法,在方法执行前切换数据源。

@Aspect
@Component
@Slf4j
@Order(-1)
public class DynamicDataSourceAspect {

    @Pointcut("@annotation(com.example.springbootpro1.dsconfig.DataSource)")
    public void dataSourcePointCut() {}

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        DataSource ds = method.getAnnotation(DataSource.class);
        if (ds == null) {
            DynamicDataSourceContextHolder.setDataSourceKey("master");
        } else {
            DynamicDataSourceContextHolder.setDataSourceKey(ds.name());
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSourceContextHolder.clearDataSourceKey();
        }
    }
}
2.4 使用注解切换数据源

最后,我们可以通过注解 @DataSource 来指定方法使用哪个数据源:

@Mapper
public interface CaseMapper {

    @Select("select * from base_cache_area")
    List<BaseCacheArea> list();

    @DataSource(name = "slave")
    List<BaseCacheArea2> selectAll();
}

结论

通过 MyBatis 配置多数据源,可以有效地应对复杂的业务需求。在性能要求高或数据源分布式的场景下,多数据源提供了灵活的解决方案。本文介绍了两种常见的多数据源配置方式:分包配置AOP 动态切换,根据实际需求选择合适的方式来实现数据源的管理与切换,从而提升系统的扩展性、可维护性及性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值