springboot3实现aop多数据源结合mybatis-plus-boot-starter

方式一

其实网上有包如下:

复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
    <version>4.3.0</version>
</dependency>

applicaiton.yml配置如下:

复制代码
spring:
  datasource:
    dynamic:
      primary: ds1
      strict: false
      # 自定义负载均衡策略。slave组下有;多个个数据源,当用户使用 slave 切换数据源时会使用负载均衡算法。系统自带了两个负载均衡算法 LoadBalanceDynamicDataSourceStrategy 轮询,是默认的。 RandomDynamicDataSourceStrategy 随机的。
      strategy: com.baomidou.dynamic.datasource.strategy.LoadBalanceDynamicDataSourceStrategy
      datasource:
        ds1:
          url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalIn
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456
        ds2:
          url: jdbc:mysql://127.0.0.1:3306/demo1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: 123456

使用的时候就简单了,直接在service中的方法上使用

复制代码
@DS("ds2")
public void saveTest() {
   this.save(TestEntity.builder()
           .name("动态数据源测试"+ ThreadLocalRandom.current().nextInt(1000))
           .build());
}

方式二:自定义aop实现

我这里使用的springboot 3.2.4

复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.4</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

application.yml,内容如下:

复制代码
spring:
  datasource:
#    driver-class-name: com.mysql.cj.jdbc.Driver
#    url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
#    username: root
#    password: 123456
#    type: com.alibaba.druid.pool.DruidDataSource
    master:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
      username: root
      password: 123456
    slave:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/demo1?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&useSSL=false&allowPublicKeyRetrieval=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=
      username: root
      password: 123456
      
# mybatis
mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  #实体扫描,多个package用逗号或者分号分隔
  typeAliasesPackage: org.demo.entity
  #typeEnumsPackage: org.springblade.dashboard.entity.enums
  global-config:
    #打开在线执行sql语句
    enable-sql-runner: true
    # 关闭MP3.0自带的banner
    banner: false
    db-config:
      #主键类型  0:"数据库ID自增", 1:"不操作", 2:"用户输入ID",3:"数字型snowflake", 4:"全局唯一ID UUID", 5:"字符串型snowflake";
      id-type: assign_id
      #字段策略
      insert-strategy: not_null
      update-strategy: not_null
      where-strategy: not_null
      #驼峰下划线转换
      table-underline: true
      # 逻辑删除配置
      # 逻辑删除全局值(1表示已删除,这也是Mybatis Plus的默认配置)
      logic-delete-value: 1
      # 逻辑未删除全局值(0表示未删除,这也是Mybatis Plus的默认配置)
      logic-not-delete-value: 0

导包

复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.16</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.5</version>
</dependency>

先创建一个自定义数据源注解DataSource.java,内容如下:

复制代码
package org.demo.RW;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
    DataSourceType value() default DataSourceType.MASTER;
}

继续创建数据源类型DataSourceType.java,内容如下:

复制代码
package org.demo.RW;

public enum DataSourceType {
    MASTER, SLAVE
}

接下来重点,springbootJDBC提供了一个抽象类 AbstractRoutingDataSource.java 用于数据源管理,只需要实现这个类,创建类DynamicDataSource.java ,内容如下:

复制代码
package org.demo.RW;


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

public class DynamicDataSource extends AbstractRoutingDataSource {

    private static final ThreadLocal<DataSourceType> CONTEXT_HOLDER = new ThreadLocal<>();

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

    public static void setDataSourceType(DataSourceType dataSourceType) {
        CONTEXT_HOLDER.set(dataSourceType);
    }

    public static DataSourceType getDataSourceType() {
        return CONTEXT_HOLDER.get();
    }

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

继续创建配置文件DataSourceConfig.java ,内容如下:

复制代码
package org.demo.RW;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@MapperScan(basePackages = "org.demo.mapper")
public class DataSourceConfig {

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

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

    @Bean
    @Primary
    public DynamicDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource,
                                        @Qualifier("slaveDataSource")DataSource slaveDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceType.MASTER, masterDataSource);
        targetDataSources.put(DataSourceType.SLAVE, slaveDataSource);

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.afterPropertiesSet();

        return dynamicDataSource;
    }

    @Bean
    @Primary
    public SqlSessionFactory sqlSessionFactory(DynamicDataSource dynamicDataSource) throws Exception {
        /*SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        return sessionFactory.getObject();*/

        MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
        sessionFactory.setDataSource(dynamicDataSource);// 这里是重点,DynamicDataSource是我们创建的数据源管理类,否则不生效

//        // 设置mapper.xml位置
//        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
//        sessionFactory.setMapperLocations(
//                resolver.getResources("classpath*:mapper/**/*.xml"));
//        sessionFactory.setTypeAliasesPackage("org.demo.entity");
//        sessionFactory.afterPropertiesSet();
        return sessionFactory.getObject();
    }

	/*  
	// 启动报错JdbcTemplate。时再打开注释,不报错不需要打开注释
	@Bean
	public JdbcTemplate jdbcTemplate(DynamicDataSource dynamicDataSource) {
		return new JdbcTemplate(dynamicDataSource);
	}

	// 事物管理
	@Bean
	public DataSourceTransactionManager transactionManager(DynamicDataSource dynamicDataSource) {
		return new DataSourceTransactionManager(dynamicDataSource);
	}*/
}

接下来创建DataSourceAspect.java 进行aop拦截,内容如下:

复制代码
package org.demo.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.demo.RW.DataSource;
import org.demo.RW.DynamicDataSource;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@EnableAspectJAutoProxy(proxyTargetClass = true)
@Aspect
@Component
@Order(1)
@Slf4j
public class DataSourceAspect {

    //任务层级目录下的service
    @Around("execution(* org.demo..service..*(..))")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        DataSource targetDataSource = method.getAnnotation(DataSource.class);
        if (targetDataSource != null) {
            DynamicDataSource.setDataSourceType(targetDataSource.value());
        }
        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSourceType();
        }
    }
}

接下来创建测试ITestService.java 与实现TestServiceImpl.java ,内容如下:

复制代码
package org.demo.a.service;

import com.baomidou.mybatisplus.extension.service.IService;
import org.demo.entity.TestEntity;

import java.util.List;

public interface ITest1Service extends IService<TestEntity> {

    List<TestEntity> getList();

    void saveTest();
}
## 实现
package org.demo.a.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.demo.RW.DataSource;
import org.demo.RW.DataSourceType;
import org.demo.a.service.ITest1Service;
import org.demo.entity.TestEntity;
import org.demo.mapper.TestMapper;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

@Service
public class Test1ServiceImpl extends ServiceImpl<TestMapper, TestEntity> implements ITest1Service {

    @Override
//    @DataSource(DataSourceType.SLAVE)
    public List<TestEntity> getList() {
        return this.list();
    }

    @Override
    @DataSource(DataSourceType.SLAVE)
    public void saveTest() {
        this.save(TestEntity.builder()
                .name("动态数据源测试"+ ThreadLocalRandom.current().nextInt(1000))
                .build());
    }
}

然后在接口里调用,Ok了。

相关推荐
while(1){yan}7 小时前
使用HuTool实现验证码
spring boot·spring·java-ee·maven
村口曹大爷7 小时前
JDK 24 正式发布:性能压轴,为下一代 LTS 铺平道路
java·开发语言
1.14(java)7 小时前
MySQL数据库操作全攻略
java·数据库·mysql
sunnyday04267 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
正远数智7 小时前
深度解析:SRM系统如何赋能采购库存协同
java·lowcode
青岛少儿编程-王老师8 小时前
CCF编程能力等级认证GESP—C++1级—20251227
java·c++·算法
hashiqimiya8 小时前
java程序的并发
java·开发语言·python
雨中飘荡的记忆8 小时前
MyBatis SQL执行模块详解
数据库·sql·mybatis
.try-8 小时前
cssTab卡片式
java·前端·javascript
ulias2128 小时前
多态理论与实践
java·开发语言·前端·c++·算法