SpringBoot 2.0+ 開(kāi)始推 HikariCP ,將默認(rèn)的數(shù)據(jù)庫(kù)連接池從 tomcat jdbc pool 改為了 hikari , HikariCP 在性能和并發(fā)方面表現(xiàn)更好一些
yml配置:
#數(shù)據(jù)源配置
datasource:
dsa:
driver-class-name: com.sybase.jdbc4.jdbc.SybDataSource
jdbc-url: jdbc:sybase:Tds:127.0.0.1:5127/test
type: com.zaxxer.hikari.HikariDataSource
username: root
password: root
dsb:
driver-class-name: com.sybase.jdbc4.jdbc.SybDataSource
jdbc-url: jdbc:sybase:Tds:127.0.0.1:5127/test
type: com.zaxxer.hikari.HikariDataSource
username: root
password: root
com.zaxxer.hikari.HikariDataSource 包為mybatis自帶
導(dǎo)入以下依賴(lài)
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
1.創(chuàng)建DBProperties
import com.zaxxer.hikari.HikariDataSource;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 實(shí)際數(shù)據(jù)源配置
*/
@Component
@Data
@ConfigurationProperties(prefix ="spring.datasource")
public class DBProperties {
private HikariDataSourcedsa;
private HikariDataSourcedsc;
private HikariDataSourcedsb;
}
2.創(chuàng)建dataSource
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* 數(shù)據(jù)源配置
*/
@Configuration
@EnableScheduling
@Slf4j
public class DataSourceConfig {
@Autowired
private DBPropertiesproperties;
@Bean(name ="dataSource")
public DataSourcedataSource() {
//按照目標(biāo)數(shù)據(jù)源名稱(chēng)和目標(biāo)數(shù)據(jù)源對(duì)象的映射存放在Map中
Map targetDataSources =new HashMap<>();
targetDataSources.put("dsa", properties.getDsa());
targetDataSources.put("dsb", properties.getDsb());
targetDataSources.put("dsc", properties.getDsc());
//采用是想AbstractRoutingDataSource的對(duì)象包裝多數(shù)據(jù)源
DynamicDataSource dataSource =new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(properties.getDsb());
return dataSource;
}
@Bean
public PlatformTransactionManagertxManager() {
return new DataSourceTransactionManager(dataSource());
}
}
3.創(chuàng)建DynamicDataSource
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 動(dòng)態(tài)數(shù)據(jù)源實(shí)現(xiàn)類(lèi)
*/
@Slf4j
public class DynamicDataSourceextends AbstractRoutingDataSource {
//數(shù)據(jù)源路由,此方用于產(chǎn)生要選取的數(shù)據(jù)源邏輯名稱(chēng)
@Override
protected ObjectdetermineCurrentLookupKey() {
//從共享線程中獲取數(shù)據(jù)源名稱(chēng)
return DynamicDataSourceHolder.getDataSource();
}
}
4.創(chuàng)建DynamicDataSourceHolder
/**
* 動(dòng)態(tài)數(shù)據(jù)源持有者,負(fù)責(zé)利用ThreadLocal存取數(shù)據(jù)源名稱(chēng)
*/
public class DynamicDataSourceHolder {
/**
* 本地線程共享對(duì)象
*/
private static final ThreadLocalTHREAD_LOCAL =new ThreadLocal<>();
public static void putDataSource(String name) {
THREAD_LOCAL.set(name);
}
public static StringgetDataSource() {
return THREAD_LOCAL.get();
}
public static void removeDataSource() {
THREAD_LOCAL.remove();
}
}
5.創(chuàng)建TargetDataSource
import java.lang.annotation.*;
/**
* 目標(biāo)數(shù)據(jù)源注解,注解在方法上指定數(shù)據(jù)源的名稱(chēng)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface TargetDataSource {
Stringvalue();//此處接收的是數(shù)據(jù)源的名稱(chēng)
}
6.創(chuàng)建DataSourceAspect
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;
/**
* 數(shù)據(jù)源AOP切面定義
*/
@Component
@Aspect
@Slf4j
public class DataSourceAspect {
//切換放在mapper接口的方法上,所以這里要配置AOP切面的切入點(diǎn)
@Pointcut("execution( * com.wondersgroup.yilian.largescreen_interface.dao.*.*(..))")
public void dataSourcePointCut() {
}
@Before("dataSourcePointCut()")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget();
String method = joinPoint.getSignature().getName();
Class[] clazz = target.getClass().getInterfaces();
Class[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
try {
Method m = clazz[0].getMethod(method, parameterTypes);
//如果方法上存在切換數(shù)據(jù)源的注解,則根據(jù)注解內(nèi)容進(jìn)行數(shù)據(jù)源切換
if (m !=null && m.isAnnotationPresent(TargetDataSource.class)) {
TargetDataSource data = m.getAnnotation(TargetDataSource.class);
String dataSourceName = data.value();
DynamicDataSourceHolder.putDataSource(dataSourceName);
//log.debug("current thread " + Thread.currentThread().getName() + " add " + dataSourceName + " 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);
}
}
//執(zhí)行完切面后,將線程共享中的數(shù)據(jù)源名稱(chēng)清空
@After("dataSourcePointCut()")
public void after(JoinPoint joinPoint){
DynamicDataSourceHolder.removeDataSource();
}
}
最后將自定義注解@TargetDataSource("dsa")根據(jù)不同的連接放在DAO層方法上。
(切記,如果在業(yè)務(wù)層加了事務(wù),數(shù)據(jù)源將無(wú)法切換)