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";
}

運行  調用即可


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章