动态数据源配置(基于注解)

1, 数据库类型

package com.mine.cloakroom.config;

/**
 * 采用枚举的方法列出所有的数据源key(常用数据库名称来命名)
 * 数据源个数和数据库个数保持一致
 */
public enum DatabaseType {
    firstdb,seconddb
}

2,动态数据源ContextHolder(需要继承AbstractRoutingDataSource)

package com.mine.cloakroom.config;

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

// 动态数据源(需要继承AbstractRoutingDataSource)
public class DynamicDataSourceContextHolder extends AbstractRoutingDataSource {
    //定义一个线程安全的DatabaseType容器
    private static final ThreadLocal<DatabaseType> contextHolder = new ThreadLocal<>();

    public static DatabaseType getDatabaseType() {
        return contextHolder.get();
    }

    public static void setDatabaseType(DatabaseType type) {
        contextHolder.set(type);
    }

    //获取当前线程的DatabaseType
    @Override
    protected Object determineCurrentLookupKey() {
        return getDatabaseType();
    }

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

}

3,动态数据源配置

package com.mine.cloakroom.config;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
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.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 动态数据源配置
 */
@Configuration
@MapperScan(basePackages = "com.mine.cloakroom.dao,com.mine.cloakroom.dao3", sqlSessionFactoryRef = "dynamicSqlSessionFactory")// sqlSessionFactoryRef 一定要加上, 指定用哪个sqlSessionFactory扫描dao
public class DynamicDataSourceConfig {

    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return new HikariDataSource();
    }

    @Bean(name = "secondDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.second")
    public DataSource secondDataSource() {
        return new HikariDataSource();
    }

    /**
     * 注入数据源
     */
    @Bean(name = "dynamicDataSource")
    public DynamicDataSourceContextHolder dataSource(@Qualifier("primaryDataSource") DataSource primaryDataSource, @Qualifier("secondDataSource") DataSource secondDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        targetDataSources.put(DatabaseType.firstdb, primaryDataSource);
        targetDataSources.put(DatabaseType.seconddb, secondDataSource);
        DynamicDataSourceContextHolder dataSource = new DynamicDataSourceContextHolder();
        dataSource.setTargetDataSources(targetDataSources);// 该方法是AbstractRoutingDataSource的方法
        dataSource.setDefaultTargetDataSource(primaryDataSource);// 默认的datasource设置为myTestDbDataSource

        return dataSource;
    }

    /**
     * 根据数据源创建SqlSessionFactory
     */
    @Bean(name = "dynamicSqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper3/*/*Mapper.xml"));
        return bean.getObject();
    }


    /**
     * 配置事务管理器
     */
    @Bean
    public DataSourceTransactionManager testTransactionManager(@Qualifier("dynamicDataSource") DynamicDataSourceContextHolder dataSource) throws Exception {
        return new DataSourceTransactionManager(dataSource);
    }
}

4,定义一个注解(使用dao方法上)

package com.mine.cloakroom.config;

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 TargetDataSource {
    DatabaseType value();
}

5,对注解切面到方法

package com.mine.cloakroom.config;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;


@Component
@Aspect
@Slf4j
public class DataSourceAspect {

    //切换放在mapper接口的方法上,所以这里要配置AOP切面的切入点
//    @Pointcut("execution( * com.mine.cloakroom.service.*.*(..))")
    @Pointcut("@annotation(com.mine.cloakroom.config.TargetDataSource)")
    public void dataSourcePointCut() {
    }

    @Before("dataSourcePointCut()")
    public void before(JoinPoint joinPoint) {
        //获得切面当中方法签名
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        //获得签名方法
        Method method = methodSignature.getMethod();
        //获得在方法上面的注解
        TargetDataSource targetDataSource =  method.getAnnotation(TargetDataSource.class);
        try {
            if(DatabaseType.firstdb.equals(targetDataSource.value()) || DatabaseType.seconddb.equals(targetDataSource.value())){
                DatabaseType type = targetDataSource.value();
                DynamicDataSourceContextHolder.setDatabaseType(type);
                log.debug("current thread " + Thread.currentThread().getName() + " add " + type + " to ThreadLocal");
            }else{
                log.debug("switch datasource fail,use default");
            }
        } catch (Exception e) {
            log.error("current thread " + Thread.currentThread().getName() + " add data to ThreadLocal error", e);
        }
    }

    //执行完切面后,将线程共享中的数据源名称清空
    @After("dataSourcePointCut()")
    public void after(JoinPoint joinPoint){
        DynamicDataSourceContextHolder.removeDataSource();
    }
}

6, dao层示例

package com.mine.cloakroom.dao3.user;

import com.mine.cloakroom.bean.entity.user.UserDO;
import com.mine.cloakroom.config.DatabaseType;
import com.mine.cloakroom.config.TargetDataSource;

import java.util.Map;

public interface UserDao3 {
    int deleteByPrimaryKey(Integer id);

    int insert(UserDO record);

    int insertSelective(UserDO record);

    @TargetDataSource(DatabaseType.seconddb)
    UserDO selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(UserDO record);

    int updateByPrimaryKey(UserDO record);

    Map<String, String> selectUserMap();
}

7,application.yml 配置

spring:
  datasource:
    primary:
      jdbc-url: jdbc:mysql://localhost:3306/cloakroom_business?characterEncoding=utf8&serverTimezone=GMT%2b8&connectTimeout=5000&socketTimeout=30000&allowMultiQueries=true
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.zaxxer.hikari.HikariDataSource
      poolName: testOneDB
      maximum-pool-size: 15
      max-lifetime: 180000
      idle-timeout: 50000
      connection-timeout: 10000
      minimum-idle: 5
      connection-test-query: SELECT 1
    second:
      jdbc-url: jdbc:mysql://198.101.566.66:3306/cloakroom_business?characterEncoding=utf8&serverTimezone=GMT%2b8&connectTimeout=5000&socketTimeout=30000&allowMultiQueries=true
      username: root
      password: 123456789
      driver-class-name: com.mysql.cj.jdbc.Driver
      type: com.zaxxer.hikari.HikariDataSource
      poolName: testSecondDB
      maximum-pool-size: 15
      max-lifetime: 180000
      idle-timeout: 50000
      connection-timeout: 10000
      minimum-idle: 5
      connection-test-query: SELECT 1

8,测试类

public class UserServiceTest {
    @Autowired
    private UserDao3 userDao3;

//    @Autowired
//    private UserDao2 userDao2;

    @Test
    public void mapTest() {
//        DynamicDataSource.setDatabaseType(DatabaseType.seconddb);// 可以直接在代码中设计数据源,也可通过注解实现
        UserDO userDO2 = userDao3.selectByPrimaryKey(2);
        System.out.println(userDO2);
        //设置数据源
//        DynamicDataSource.setDatabaseType(DatabaseType.firstdb);
        UserDO userDO = userDao3.selectByPrimaryKey(2);
        System.out.println(userDO.toString());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值