springboot(六、多源数据库【动态】)

所谓的动态的多源数据库的配置,是一个DataSource合并  共用一个sqlsessionfactory

1、大概的结构


用到的jar包


2、com.example.entity ;com.example.dao.mapper;com.example.service;com.example.service.impl;这些包里面的东西跟平时写的没啥区别,关键的包com.example.config 这里面有两个类 一个是配置DataSource  一个是sqlsessionfactory

/**
 * DataSource
 * @author Administrator
 *
 */
@Configuration
public class DataSourceConfig {
    @Autowired
    Environment env;
 
    public DataSource dataSource1() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.write.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.write.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.write.datasource.password"));
        dataSource.setDriverClassName(env.getProperty("spring.write.datasource.driverClassName"));
        return dataSource;
    }
 
    public DataSource dataSource2() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.read.one.url"));
        dataSource.setUsername(env.getProperty("spring.read.one.username"));
        dataSource.setPassword(env.getProperty("spring.read.one.password"));
        dataSource.setDriverClassName(env.getProperty("spring.read.one.driverClassName"));
        return dataSource;
    }
 
    public DataSource dataSource3() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.read.two.url"));
        dataSource.setUsername(env.getProperty("spring.read.two.username"));
        dataSource.setPassword(env.getProperty("spring.read.two.password"));
        dataSource.setDriverClassName(env.getProperty("spring.read.two.driverClassName"));
        return dataSource;
    }
 
    public DataSource dataSource4() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(env.getProperty("spring.read.three.url"));
        dataSource.setUsername(env.getProperty("spring.read.three.username"));
        dataSource.setPassword(env.getProperty("spring.read.three.password"));
        dataSource.setDriverClassName(env.getProperty("spring.read.three.driverClassName"));
        return dataSource;
    }
    @Bean(name = "dynamicDS")
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 默认数据源,当没有指定的时候使用,可以当做主数据源
       dynamicDataSource.setDefaultTargetDataSource(dataSource1());

        Map<Object, Object> dsMap = new HashMap();
        dsMap.put("dataSource1", dataSource1());
        dsMap.put("dataSource2", dataSource2());
        dsMap.put("dataSource3", dataSource3());
        dsMap.put("dataSource4", dataSource4());
        // 注册多数据源
        dynamicDataSource.setTargetDataSources(dsMap);
        return dynamicDataSource;
    }

}

/**
 * sqlSessionFactory
 * @author Administrator
 *
 */
@Configuration
@MapperScan(basePackages = {"com.example.dao.mapper"})
public class MybatisDbConfig {

    @Qualifier("dynamicDS")
    @Autowired
    DataSource dataSource;

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

    }

    @Bean
    public SqlSessionTemplate sqlSessionTemplate1() throws Exception {
        SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory()); // 使用上面配置的Factory
        return template;
    }
}

3、com.example.datasource  侧重于数据切换

public class DataSourceContextHolder {
	 //默认数据源
	 public static final String DEFAULT_DS = "dataSource1";
	 private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
	 public static final Logger log = LoggerFactory.getLogger(DataSourceContextHolder.class);
	 	// 设置数据源标识
	    public static void setDataSource(String dbType) {
	    	log.info("切换到{}数据源", dbType);
	        contextHolder.set(dbType);
	    }

	    // 获取数据源标识
	    public static String getDataSource() {
	        return (contextHolder.get());
	    }

	    // 清除数据源标识
	    public static void clearDataSource() {
	        contextHolder.remove();
	    }
	    
}
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSource.class);

    /**
     *DynamicDataSourceContextHolder代码中使用setDataSource 
     * 设置当前的数据源,在路由类中使用getDataSource进行获取,
     *  交给AbstractRoutingDataSource进行注入使用。
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        log.info("数据源为===>{}", DataSourceContextHolder.getDataSource());
        return DataSourceContextHolder.getDataSource();
    }
}

@Aspect
@Component
@Order(value=-1)//保证该AOP在@Transactional之前执行
public class DynamicDataSourceAspect {

    /**
     * 定义拦截规则
     */
  @Pointcut("within(com.example.service.impl.*)")
    public void pointCut(){}

    /**
     * @Before:在方法执行之前进行执行:
     * @annotation(targetDataSource):
     * 会拦截注解targetDataSource的方法,否则不拦截;
     *  
     */
    @Before("@annotation(TargetDataSource)") //这样注解在类上会扫描不到
    public void beforeDynamicDataSource(JoinPoint point){
        /*类*/
        Class<?> targetClass = point.getTarget().getClass();
        /*方法名*/
        String methodName = point.getSignature().getName();
        /*方法的参数的类型*/
        Class[] parameterTypes = ((MethodSignature)point.getSignature()).getParameterTypes();

        String dataSourceType = DataSourceContextHolder.DEFAULT_DS;
        try {
            /*默认使用类型注解*/
            if (targetClass.isAnnotationPresent(TargetDataSource.class)) {
                TargetDataSource annotation = targetClass.getAnnotation(TargetDataSource.class);
                dataSourceType = annotation.value();
            }
            /*由方法名和参数获取该类下的目标方法对象 */
            Method method = targetClass.getMethod(methodName, parameterTypes);
            /*方法注解可以覆盖类型注解*/
            if (method!=null && method.isAnnotationPresent(TargetDataSource.class)) {
                TargetDataSource annotation = method.getAnnotation(TargetDataSource.class);
                dataSourceType = annotation.value();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*切换数据源*/
        DataSourceContextHolder.setDataSource(dataSourceType);
        

    }

    /**
     * 使用完后清理
     *  
     */
    @After("@annotation(TargetDataSource)")
    public void afterDynamicDataSource(JoinPoint point){
        DataSourceContextHolder.clearDataSource();
    }
}

//自定义注解 通过@TargetDataSource("dataSource2")知道数据源的切换  放serviceImpl 也可以放controller 根据需求
@Retention(RetentionPolicy.RUNTIME)
@Target({
	ElementType.METHOD,ElementType.TYPE
})
public @interface TargetDataSource {
    String value() default "dataSource1";
}

运行  调用即可


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值