AOP 方式实现Spingboot +Mybatis 数据库多写

配置pom.xml



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.5.RELEASE
        
    
    yougroup
    demo
    0.0.1-SNAPSHOT
    bom
    demo
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.4.3.1
        
   
        
            mysql
            mysql-connector-java
            8.0.26
        
       
        
            org.springframework.boot
            spring-boot-starter-aop
        

        
            com.alibaba
            fastjson
            2.0.7
        
      
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
            
            
                org.mybatis.generator
                mybatis-generator-maven-plugin
                1.3.5
                
                    
                    src/main/resources/generatorConfig.xml
                    true
                    true
                
                
                    
                        com.itfsw
                        mybatis-generator-plugin
                        1.3.5
                    
                

            
        
    


application.yml增加数据库配置

server:
  port: 8080
  servlet:
    context-path:
spring:
  datasource:
    db1:
      jdbc-url: jdbc:mysql://localhost:3306/db1?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true
      username: root
      password: root
    db2:
      jdbc-url: jdbc:mysql://localhost:3306/db1?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true
      username: root
      password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
logging:
  config: classpath:logback-spring.xml
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

增加datasource配置类

@Configuration
public class DataSourceConfig {
    /**
     * 数据源1
     * spring.datasource.db1:application.yml中对应属性的前缀
     * @return
     */
    @Bean(name = "db1")
    @ConfigurationProperties(prefix = "spring.datasource.db1")
    public DataSource dataSourceOne() {
        return DataSourceBuilder.create().build();
    }

    /**
     * 数据源2
     * spring.datasource.db2:application.yml中对应属性的前缀
     * @return
     */
    @Bean(name = "db2")
    @ConfigurationProperties(prefix = "spring.datasource.db2")
    public DataSource dataSourceTwo() {
        return DataSourceBuilder.create().build();
    }

    /**
     * @return
     */
    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSourceOne());
        // 配置多数据源
        Map dsMap = new HashMap<>();
        dsMap.put("db1", dataSourceOne());
        dsMap.put("db2", dataSourceTwo());

        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

    /**
     * 配置多数据源后IOC中存在多个数据源了,事务管理器需要重新配置,不然器不知道选择哪个数据源
     * 事务管理器此时管理的数据源将是动态数据源dynamicDataSource
     * 配置@Transactional注解
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }

DynamicDataSource类


import own.utils.DataSourceUtil;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * @author 
 * @date 2023/4/25 14:56
 * @desc
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceUtil.getDB();
    }
}

增加注解类


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

/**
 * 数据库多写注解
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MultiWrite {
    String[] databases();
}

增加线程资源类用于选择数据源

public class DataSourceUtil {
    /**
     *  数据源属于一个公共的资源
     *  采用ThreadLocal可以保证在多线程情况下线程隔离
     */
    private static final ThreadLocal contextHolder = new ThreadLocal<>();

    /**
     * 设置数据源名
     * @param dbType
     */
    public static void setDB(String dbType) {
        contextHolder.set(dbType);
    }

    /**
     * 获取数据源名
     * @return
     */
    public static String getDB() {
        return (contextHolder.get());
    }

    /**
     * 清除数据源名
     */
    public static void clearDB() {
        contextHolder.remove();
    }

增加切面处理类



import com.alibaba.fastjson.JSON;
import own.commons.MultiWrite;
import own.utils.DataSourceUtil;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author 
 * @date 2023/4/25 16:22
 * @desc
 */
@Slf4j
@Aspect
@Component
public class DataMultiWrite {
    @Pointcut("@annotation(own.commons.MultiWrite)")
    private void around(){}

    @Around("around()")
    private Object write(ProceedingJoinPoint point) throws Throwable {
        //方法名
        String methodName = point.getSignature().getName();
        // 反射获取目标类
        Class targetClass = point.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型(重载)获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        MultiWrite annotation = objMethod.getDeclaredAnnotation(MultiWrite.class);
        String[] databases=  annotation.databases();
        Object res=null;
        for(String database:databases){
            DataSourceUtil.setDB(database);
            res = point.proceed();
            log.info("database :{} {}",database, JSON.toJSONString(res));
        }
        return res;
    }
}

在需要多写的方法中增加注解

@MultiWrite(databases = {"db1","db2"})
public void save(User user) {
    userMapper.save(user);
}

修改启动类并关闭自带的数据源,然后启动测试


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;


@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

你可能感兴趣的:(数据库,java,spring)