在我們?nèi)粘i_發(fā)中大多數(shù)情況基本都是使用一個(gè)數(shù)據(jù)庫(kù)就完事了辑莫,但是也難免會(huì)遇到多數(shù)據(jù)庫(kù)的情況褐荷,這個(gè)時(shí)候多數(shù)據(jù)源就派上用場(chǎng)了弄痹。當(dāng)然了饭入,多數(shù)據(jù)源有很多的ORM框架都提供了方案,比如MybatisPlus肛真,MybatisFlex等谐丢,但是作為開發(fā)者的我們不僅要學(xué)會(huì)用輪子,也要知道一二吧蚓让,不能老拿著別人的輪子跑乾忱,不去了解和學(xué)習(xí)其原理吧。那么接下來(lái)历极,我就基于springboot分享一下如何定義自己的多數(shù)據(jù)源窄瘟。
第一步: 搭建springboot項(xiàng)目,配置yml, 比如我這里就定義兩個(gè)數(shù)據(jù)源即:db1與db2,注意這里和springboot默認(rèn)數(shù)據(jù)源配置有區(qū)別哦趟卸,是jdbc-url不是url蹄葱。
spring:
datasource:
db1:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8
username: 你的用戶名
password: 你的密碼
db2:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db2?useUnicode=true&characterEncoding=utf-8
username: 你的用戶名
password: 你的密碼
第二步: 開始擼碼
1.定義數(shù)據(jù)源枚舉,如還有可以繼續(xù)定義锄列。
public enum DataSourceType {
//數(shù)據(jù)源1
DB1,
//數(shù)據(jù)源2
DB2;
}
- 定義一個(gè)數(shù)據(jù)源切換上下文輔助類
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> LOCAL = new ThreadLocal<>();
// 設(shè)置數(shù)據(jù)源
public static void setDataSourceType(String type) {
LOCAL.set(type);
}
//獲取數(shù)據(jù)源
public static String getDataSourceType() {
return LOCAL.get();
}
//清除數(shù)據(jù)源
public static void clearDataSourceType() {
LOCAL.remove();
}
}
3.定義一個(gè)名為DynamicDatasource的類繼承spring提供的AbstractRoutingDataSource類
public class DynamicDataSource extends AbstractRoutingDataSource {
//通過(guò)數(shù)據(jù)源上下文輔助類拿到數(shù)據(jù)源類型作為理由key
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
- 注入數(shù)據(jù)源,排除springboot自動(dòng)配置數(shù)據(jù)源图云,不然啟動(dòng)會(huì)報(bào)找不到數(shù)據(jù)源的錯(cuò)誤
@Configuration
@EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
public class DynamicConfig {
// 數(shù)據(jù)1
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db1")
public DataSource db1DataSource(){
return DataSourceBuilder.create().build();
}
// 數(shù)據(jù)2
@Bean
@ConfigurationProperties(prefix = "spring.datasource.db2")
public DataSource db2DataSource(){
return DataSourceBuilder.create().build();
}
//主數(shù)據(jù)源及數(shù)據(jù)源列表配置
@Bean
@Primary
public DataSource dynamicDataSource(){
final DynamicDataSource dynamicDataSource = new DynamicDataSource();
final HashMap<Object, Object> targetDataSources = new HashMap<>(2);
final DataSource db1DataSource = db1DataSource();
final DataSource dbed2DataSource = db2DataSource();
targetDataSources.put(DataSourceType.DB1.name(),db1DataSource);
targetDataSources.put(DataSourceType.DB2.name(),dbed2DataSource);
dynamicDataSource.setTargetDataSources(targetDataSources);
//設(shè)置默認(rèn)數(shù)據(jù)源
dynamicDataSource.setDefaultTargetDataSource(db1DataSource);
return dynamicDataSource;
}
}
- 至此就完成了代碼的編寫,接下來(lái)測(cè)試多數(shù)據(jù)源
final List<User> list = userMapper.list();
log.info("=====db1===={}",list);
//通過(guò)上下文輔助類切換數(shù)據(jù)源
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.DB2.name());
final List<User> list2 = userMapper.list();
log.info("======db2==={}",list2);
運(yùn)行起能夠查詢兩個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)就說(shuō)明ok了,功能上雖然沒(méi)有問(wèn)題了邻邮,但是還是不夠優(yōu)雅竣况,我們接下來(lái)要實(shí)現(xiàn)更優(yōu)雅的數(shù)據(jù)源切換。
第三步:快碼加鞭筒严,使用自定義注解加Aop實(shí)現(xiàn)更優(yōu)雅的數(shù)據(jù)源切換丹泉。
1.定義注解
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
//默認(rèn)數(shù)據(jù)源為db1
DataSourceType value() default DataSourceType.DB1;
}
2.定義aop
@Aspect
@Component
public class DataSourceAop {
/**
* 定義切點(diǎn)為自定義注解
*/
@Pointcut("@annotation(com.example.datasourcedemo.dynamic.annotation.DataSource)")
public void pointCut(){
}
/**
* 切換數(shù)據(jù)源
* @param point
*/
@Before("pointCut()")
public void changeDataSource(JoinPoint point){
final MethodSignature signature = (MethodSignature) point.getSignature();
final Method method = signature.getMethod();
final DataSource annotation = method.getAnnotation(DataSource.class);
final DataSourceType type = annotation.value();
if(!type.name().equals(DynamicDataSourceContextHolder.getDataSourceType())){
DynamicDataSourceContextHolder.setDataSourceType(type.name());
}
}
/**
* 執(zhí)行后恢復(fù)為默認(rèn)數(shù)據(jù)源
* @param point
*/
@After("pointCut()")
public void restDataSource(JoinPoint point){
if(!DataSourceType.DB1.name().equals(DynamicDataSourceContextHolder.getDataSourceType())){
DynamicDataSourceContextHolder.setDataSourceType(DataSourceType.DB1.name());
}
}
- 在測(cè)試的mapper上加上注解, 再進(jìn)行測(cè)試
@Mapper
public interface UserMapper {
@Select("select * from user")
List<User> list();
//用上自定義注解
@DataSource(DataSourceType.DB2)
@Select("select * from user")
List<User> list2();
}
去除之前測(cè)試代碼中的上下文輔助切換方式,再次運(yùn)行情萤,結(jié)果一致就說(shuō)明ok了。
final List<User> list = userMapper.list();
log.info("=====db1===={}",list);
final List<User> list2 = userMapper.list2();
log.info("=====db2===={}",list2);
總結(jié)摹恨,通過(guò)以上三步就完成了springboot自定義多數(shù)據(jù)源紫岩,是不是也沒(méi)有想象中那么難。輪子雖好睬塌,但是我們作為開發(fā)者也要學(xué)習(xí)實(shí)現(xiàn),不能老是拿來(lái)主義歇万,知其然不知其所以然揩晴,這樣永遠(yuǎn)也突破不了自己。今天就分享到這里了贪磺,有什么問(wèn)題歡迎留言探討或批評(píng)指正硫兰,如果喜歡我的文章記得關(guān)注我哦??!