自定義註解完成資料庫切庫
第一步定義我們自定義切庫註解類(使用spring boot 實現)
- 自定義註解有幾點需要注意:
1、@Target 是作用的目標,介面、方法、類、欄位、包等等,具體看:ElementType
2、@Retention 是註解存在的範圍,RUNTIME代表的是註解會在class位元組碼檔案中存在,在執行時可以通過反射獲取到,具體看:RetentionPolicy
3、允許的變數,通常都要給定預設值,比如我們使用一個service時,可以@Service,也可以@Service("xxxx")
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD })
public @interface RoutingDataSource {
String value() default DataSources.MASTER_DB;
}
- 定義需要使用的資料庫及配置
1、資料庫配置:application.properties,這裡要注意不同db的字首區別
## datasource master #
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/master?characterEncoding=UTF-8
spring.datasource.username=root spring.datasource.password=466420182
## datasource slave #
spring.datasourceSlave.type=com.alibaba.druid.pool.DruidDataSource
spring.datasourceSlave.driver-class-name=com.mysql.jdbc.Driver
spring.datasourceSlave.url=jdbc:mysql://localhost:3306/slave?characterEncoding=UTF-8
spring.datasourceSlave.username=root
spring.datasourceSlave.password=466420182
2、定義資料來源id
public interface DataSources {
String MASTER_DB = "masterDB";
String SLAVE_DB = "slaveDB";
}
3、定義資料庫實體類並配置為多資料來源的形式 ,這裡不要忽略了通過 MapperScan 指定需要掃描的mybatis的介面類
@Configuration
public class DatasourceConfig {
//destroy-method="close"的作用是當資料庫連線不使用的時候,就把該連線重新放到資料池中,方便下次使用呼叫.
@Bean(destroyMethod = "close", name = DataSources.MASTER_DB)
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
@Bean(destroyMethod = "close", name = DataSources.SLAVE_DB)
@ConfigurationProperties(prefix = "spring.datasourceSlave")
public DataSource dataSourceSlave() {
return DataSourceBuilder.create().type(DruidDataSource.class).build();
}
}
4、配置成動態資料來源:
@Configuration
@MapperScan(basePackages = {"com.xxx.dao"}) // 這裡需要替換為實際的路徑
public class MybatisConfig {
@Autowired
@Qualifier(Datasources.MASTER_DB)
private DataSource masterDB;
@Autowired
@Qualifier(DataSources.SLAVE_DB)
private DataSource slaveDB;
/**
* 動態資料來源
*/
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
// 預設資料來源
dynamicDataSource.setDefaultTargetDataSource(masterDB);
// 配置多資料來源
Map<Object, Object> dsMap = Maps.newHashMap();
dsMap.put(DataSources.MASTER_DB, masterDB);
dsMap.put(DataSources.SLAVE_DB, slaveDB);
dynamicDataSource.setTargetDataSources(dsMap);
return dynamicDataSource;
}
@Bean
@ConfigurationProperties(prefix = "mybatis")
public SqlSessionFactoryBean sqlSessionFactoryBean() {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
// 配置資料來源,此處配置為關鍵配置,如果沒有將 dynamicDataSource 作為資料來源則不能實現切換
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
return sqlSessionFactoryBean;
}
}
- 使用ThreadLocal安全的管理當前程式使用的資料來源連線
@Slf4j
public class DataSourceContextHolder {
/**
* 預設資料來源
*/
public static final String DEFAULT_DATASOURCE = DataSources.MASTER_DB;
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
// 設定資料來源名
public static void setDB(String dbType) {
log.debug("切換到{}資料來源", dbType);
contextHolder.set(dbType);
}
// 獲取資料來源名
public static String getDB() {
return (contextHolder.get());
}
// 清除資料來源名
public static void clearDB() {
contextHolder.remove();
}
}
- 通過編寫切面,對所有我們自定義切庫註解的方法進行攔截,動態的選擇資料來源
1、這裡是為下一步提供鋪墊,動態調整DataSourceContextHolder裡儲存的值,使用threadLocal來管理是為了避免多執行緒之間互相影響。
2、自定義註解,核心的處理就是寫處理這個註解的邏輯,然後通過指定的攔截方案根據當前的資料做一些動態的處理。比如Spring提供的@Controller、@Service等註解,都是需要我們在配置檔案裡配置好需要掃描的路徑,然後專案啟動時,spring根據配置去指定路徑讀取這些配置,然後這些類才可以被spring進行管理。
3、這裡不要忽略了預設資料來源要選擇主庫,如果切庫出現什麼問題,比如配置錯誤等,可以保證訪問主庫來得到正確的結果;另外,請求完了不要忘記呼叫提供的clearDB的操作,防止threadLocal誤用帶來的記憶體洩露。
@Aspect
@Component
@Slf4j
public class DynamicDataSourceAspect {
@Before("@annotation(RoutingDataSource)")
public void beforeSwitchDS(JoinPoint point){
//獲得當前訪問的
class Class<?> className = point.getTarget().getClass();
//獲得訪問的方法名
String methodName = point.getSignature().getName();
//得到方法的引數的型別
Class[] argClass = ((MethodSignature)point.getSignature()).getParameterTypes();
String dataSource = DataSourceContextHolder.DEFAULT_DATASOURCE;
try {
// 得到訪問的方法物件
Method method = className.getMethod(methodName, argClass);
// 判斷是否存在@DS註解
if (method.isAnnotationPresent(RoutingDataSource.class)) {
RoutingDataSource annotation = method.getAnnotation(RoutingDataSource.class);
// 取出註解中的資料來源名
dataSource = annotation.value();
}
} catch (Exception e) {
log.error("routing datasource exception, " + methodName, e);
}
// 切換資料來源
DataSourceContextHolder.setDB(dataSource);
}
@After("@annotation(RoutingDataSource)")
public void afterSwitchDS(JoinPoint point){
DataSourceContextHolder.clearDB();
}
}
- 動態的取出我們在切面裡設定的資料來源的字串即可
這裡需要把原理介紹一下,在連線資料庫時其實是先選擇一個配置好的spring管理的datasource的id,就是我們之前在 DatasourceConfig 類裡定義的Datasource實體類的id:masterDB 和 slaveDB。然後根據id去spring的上下文選擇配置,進行資料庫連線。有興趣的可以看一下原始碼。
@Slf4j
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
log.debug("資料來源為{}", DataSourceContextHolder.getDB());
return DataSourceContextHolder.getDB();
}
}
如何使用呢,我們簡單演示一下:
@Service
public class DataSourceRoutingService {
@Resource
private SysUserMapper sysUserMapper;
@RoutingDataSource(DataSources.MASTER_DB) // 這個註解這時是可以省略,因為預設就是訪問主庫
public SysUser test1(int id) {
return sysUserMapper.selectByPrimaryKey(id);
}
@RoutingDataSource(DataSources.SLAVE_DB)
public SysUser test2(int id) {
return sysUserMapper.selectByPrimaryKey(id);
}
}
如此,資料庫切庫就OK了。如果你的系統已經有主庫、從庫之分了,那麼趕緊在你的系統裡利用起來吧。
擴充套件
這裡呢,還可以支援多個擴充套件。比如現在一個主庫後面有多個從庫,在切面拿到需要切換從庫時,還可以選擇隨機選擇一個,或者根據類名、方法名或業務配置等選擇某一個從庫,這樣不但可以分擔每個從庫的壓力,也可以有針對性的讓指定的讀請求打到指定的從庫上。
如果有多個主庫,也可以有更多的選擇~
相關文章
- Spring Boot中自定義註解+AOP實現主備庫切換Spring Boot
- SpringBoot框架:通過AOP和自定義註解完成druid連線池的動態資料來源切換(三)Spring Boot框架UI
- 自定義註解進行資料脫敏
- 自定義註解
- Java註解-後設資料、註解分類、內建註解和自定義註解Java
- 自定義開發資料庫升級程式資料庫
- 瀚高資料庫自定義操作符'!~~'資料庫
- 自定義ConditionalOnXX註解
- 自定義JAVA註解Java
- Java中的註解-自定義註解Java
- 自定義帶監控的資料庫連線池資料庫
- 工作常備:自定義註解實現資料脫敏
- Aop+自定義註解實現資料字典翻譯
- 【MySQL】自定義資料庫連線池和開源資料庫連線池的使用MySql資料庫
- 【解決方案】基於資料庫驅動的自定義 TypeHandler 處理器資料庫
- Django切換MySQL資料庫DjangoMySql資料庫
- JAVA-註解(2)-自定義註解及反射註解Java反射
- thinkphp6 使用自定義命令,生成資料庫檢視PHP資料庫
- Kettle自定義資料庫連線型別連線HGDB資料庫型別
- java中如何自定義註解Java
- Spring Boot 自定義註解失效Spring Boot
- SpringBoot自定義校驗註解Spring Boot
- 自定義校驗註解ConstraintValidatorAI
- springBoot自定義註解的使用Spring Boot
- 如何在MySQL資料庫中使用use來切換資料庫?MySql資料庫
- 註解切換雙資料來源
- Pytorch技法:繼承Subset類完成自定義資料拆分PyTorch繼承
- Spring Boot中使用JPA呼叫自定義的資料庫函式Spring Boot資料庫函式
- Android圖解建立外部lib庫及自定義ViewAndroid圖解View
- 使用AOP+自定義註解完成spring boot的介面許可權校驗Spring Boot
- 自定義註解以及註解在反射中的應用反射
- flink 透過繼承RichSinkFunction實現自定義sink,將資料錄入資料庫繼承Function資料庫
- Mysql資料庫自定義函式的定義、使用方法及操作注意事項MySql資料庫函式
- Java 自定義註解及使用場景Java
- SpringBoot自定義註解、AOP列印日誌Spring Boot
- app直播原始碼,java自定義註解APP原始碼Java
- Rust引用自定義c/c++庫RustC++
- java自定義註解學習(三)_註解解析及應用Java