利用aop注解实现多源数据库切换,具体步骤如下:
1、数据源切换工具类 ThreadLocal
public class DynamicDataSourceContextHolder {
/**
* 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
}
public static String getDataSourceType(){
return contextHolder.get();
}
public static void clearDataSourceType(){
contextHolder.remove();
}
}
2、动态数据源类
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* @author
* 动态数据源
**/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 代码中的determineCurrentLookupKey方法取得一个字符串,
* 该字符串将与配置文件中的相应字符串进行匹配以定位数据源,配置文件,
* 即applicationContext.xml文件中需要要如下代码:(non-Javadoc)
*
* @see AbstractRoutingDataSource#determineCurrentLookupKey()
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
/*
* DynamicDataSourceContextHolder代码中使用setDataSourceType
* 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
* 交给AbstractRoutingDataSource进行注入使用。
*/
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
3、切换数据源的注解
import java.lang.annotation.*;
/**
* @author
**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}
4、处理数据源切换的AOP切面
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
* @author
* 切换数据源Advice
**/
@Aspect
@Order(-10) //保证该aop在@Transaction之前执行
@Component
public class DynamicDataSourceAspect {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* * @Before("@annotation(ds)")
* 的意思是:@Before:在方法执行之前进行执行; @annotation(targetDataSource):会拦截注解targetDataSource的方法,否则不拦截;
* @param point
* @param targetDataSource
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource){
//获取当前的指定数据源
String dsID = targetDataSource.value();
DynamicDataSourceContextHolder.setDataSourceType(dsID);
}
@After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource){
//方法执行完毕后,销毁当前数据源信息,进行垃圾回收
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
5、数据源配置
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;
import io.shardingsphere.api.config.rule.ShardingRuleConfiguration;
import io.shardingsphere.api.config.rule.TableRuleConfiguration;
import io.shardingsphere.api.config.strategy.InlineShardingStrategyConfiguration;
import io.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.ibatis.datasource.pooled.PooledDataSource;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import sf.myboot.dynamicdata.DynamicDataSource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
public class ShardingJDBCConfiguration {
//数据源1
@Bean(name ="ds_0")
public DataSource dataSource0(){
PooledDataSource dataSource = new PooledDataSource();
try{
dataSource.setUsername("root");
dataSource.setPassword("");
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mytest?serverTimezone=UTC");
} catch (Exception e){
}
return dataSource;
}
//数据源2
@Bean(name ="ds_1")
public DataSource dataSource1(){
PooledDataSource dataSource = new PooledDataSource();
try{
dataSource.setUsername("root");
dataSource.setPassword("");
dataSource.setDriver("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mytest2?serverTimezone=UTC");
} catch (Exception e){
}
return dataSource;
}
//对数据源1实现分表设置
@Bean(name="shardingDataSource")
public DataSource getDataSource(@Qualifier("ds_0") DataSource ds_0) throws SQLException {
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
//分表策略
shardingRuleConfig.getTableRuleConfigs().add(remarkRuleConfig());
// shardingRuleConfig.getTableRuleConfigs().add(taskRuleConfig());
//分库设置
Map<String, DataSource> dataSourceMap = new HashMap<>();
dataSourceMap.put("ds_0", ds_0);
//设置默认的数据源
// shardingRuleConfig.setDefaultDataSourceName("ds_0");
Properties properties = new Properties();
properties.setProperty("sql.show", Boolean.TRUE.toString());
return ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap<>(), properties);
}
private TableRuleConfiguration remarkRuleConfig() {
// 配置表规则
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
tableRuleConfig.setLogicTable("logtable");
tableRuleConfig.setActualDataNodes("ds_0.logtable_${1..2}");
// tableRuleConfig.setKeyGeneratorColumnName("id");
// 配置分表策略
tableRuleConfig.setTableShardingStrategyConfig(new InlineShardingStrategyConfiguration("remark", "logtable_${remark % 2}"));
return tableRuleConfig;
}
//加载所有数据源
@Bean
@Primary
public DynamicDataSource dataSource(
@Qualifier("shardingDataSource")DataSource shardingDataSource,
@Qualifier("ds_1")DataSource ds1) {
Map<Object, Object> targetDataSource=new HashMap<Object, Object>();
targetDataSource.put("sharding",shardingDataSource);
targetDataSource.put("ds1",ds1);
DynamicDataSource dynamicDataSource=new DynamicDataSource();
dynamicDataSource.setTargetDataSources(targetDataSource);
dynamicDataSource.setDefaultTargetDataSource(shardingDataSource);
return dynamicDataSource;
}
//多源数据与Mybatis plus配置
@Bean(name = "shardSqlSessionFactory")
public MybatisSqlSessionFactoryBean shardSqlSessionFactory(@Qualifier("shardingDataSource") DataSource dataSource,@Qualifier("ds_1") DataSource ds1) throws Exception {
MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean ();//MybatisSqlSessionFactoryBean为Mybatis Plus的bean
bean.setDataSource(this.dataSource(dataSource,ds1));
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
return bean;
}
}
6、使用注解动态切换数据源
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper,Student> implements StudentService {
@Override
@TargetDataSource("ds1")
public Student getStudent(Long id) {
Wrapper<Student> wrapper=new EntityWrapper<>();
wrapper.eq("id",id);
return selectOne(wrapper);
}
}