springboot+mybatis+druid實現mysql主從讀寫分離(五)
文章目錄
一、如何自定義註解
1、使用格式
-
修飾符
訪問修飾符必須為public,不寫預設為public
-
關鍵字
關鍵字為@interface;
-
註解名稱
自定義名稱
-
註解型別元素
註解型別元素是註解中內容,可以理解成自定義介面的實現部分;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSign {
/**
* 切換資料來源名稱
*/
//返回型別 方法 預設值。
DbType value() default DbType.MASTER;
}
2、使用元註解
JDK中有一些元註解,主要有@Target,@Retention,@Document,@Inherited用來修飾註解。
二、資料來源配置
spring:
datasource:
master:
jdbc-url: jdbc:mysql://192.168.102.31:3306/test
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
slave1:
jdbc-url: jdbc:mysql://192.168.102.56:3306/test
username: pig # 只讀賬戶
password: 123456
driver-class-name: com.mysql.j
dbc.Driver
三、主從分離程式碼實現
1、資料來源型別
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSourceSign {
/**
* 切換資料來源名稱
*/
DbType value() default DbType.MASTER;
}
2、設定獲取資料來源
public class DynamicDbContextHolder {
public enum DbType {
MASTER, SLAVE, OTHER
}
/**
* 使用ThreadLocal維護變數,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,
* 所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。
*/
private static final ThreadLocal<DbType> contextHolder = new ThreadLocal<DbType>();
/**
* 設定資料來源變數
* @param dbType
*/
public static void setDbType(DbType dbType) {
if (dbType == null) {
throw new NullPointerException();
}
contextHolder.set(dbType);
}
/**
* 獲取資料來源變數
* @return
*/
public static DbType getDbType() {
return contextHolder.get() == null ? DbType.MASTER : contextHolder.get();
}
/**
* 清空資料來源變數
*/
public static void clearDbType() {
contextHolder.remove();
}
}
3、自定義資料來源註解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSourceSign {
DbTypeEnum value() default DbTypeEnum.MASTER;
}
4、動態切換資料來源AOP切面處理
/**
* 問題:自定義資料來源註解與@Transaction同時使用時不生效
* 自定義資料來源註解與@Transaction註解同一個方法,會先執行@Transaction註解,即獲取資料來源在切換資料來源之前,所以會導致自定義註解失效。
* 解決方法:定義切換資料來源的註解的AOP切面(DynamicDataSourceAspect )上新增註解【@Order(-1),ordel的value越小,就越先執行】,
* 保證該AOP在@Transactional之前執行
*/
@Aspect
@Order(-1)
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.jht.jsicp.common.dynamicdb.annotation.DataSourceSign)")
public void dsPointCut() {
}
@Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSourceSign dataSource = method.getAnnotation(DataSourceSign.class);
if (dataSource != null) {
DynamicDbContextHolder.setDbType(dataSource.value());
}
try {
return point.proceed();
} finally {
// 銷燬資料來源 在執行方法之後
DynamicDbContextHolder.clearDbType();
}
}
}
——————————————————————————————————另一種寫法———————————————————————————————————————————————
@Aspect
@Component
public class DynamicDbAspect implements PriorityOrdered {
public static final Logger logger = LoggerFactory.getLogger(DynamicDbAspect.class);
/**
* 切換到master主庫
* @param proceedingJoinPoint
* @param Page
* @return
* @throws Throwable
*/
@Around("@annotation(master)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Master master) throws Throwable {
try {
//logger.info("set database connection to master only");
DynamicDbContextHolder.setDbType(DynamicDbContextHolder.DbType.MASTER);
Object result = proceedingJoinPoint.proceed();
return result;
} finally {
DynamicDbContextHolder.clearDbType();
//logger.info("restore master database connection");
}
}/**
* 切換到slave從庫
* @param proceedingJoinPoint
* @param Slave
* @return
* @throws Throwable
*/
@Around("@annotation(slave)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint, Slave slave) throws Throwable {
try {
//logger.info("set database connection to slave only");
DynamicDbContextHolder.setDbType(DynamicDbContextHolder.DbType.SLAVE);
Object result = proceedingJoinPoint.proceed();
return result;
} finally {
DynamicDbContextHolder.clearDbType();
//logger.info("restore slave database connection");
}
}
@Override
public int getOrder() {
return 1;
}
}
5、動態資料來源決策
Spring boot提供了AbstractRoutingDataSource 根據使用者定義的規則選擇當前的資料來源,這樣我們可以在執行查詢之前,設定使用的資料來源。實現可動態路由的資料來源,在每次資料庫查詢操作前執行。它的抽象方法 determineCurrentLookupKey() 決定使用哪個資料來源。
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
public static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
return DynamicDbContextHolder.getDbType();
}
}
6、資料庫(源)、事務配置類
//參考
@Configuration
@EnableTransactionManagement
public class DataSourceConfiguration {
public static final Logger logger = LoggerFactory.getLogger(DataSourceConfiguration.class);
@Value("${druid.type}") #druid.type=com.alibaba.druid.pool.DruidDataSource
private Class<? extends DataSource> dataSourceType;
#mybatis配置
#mybatis.mapper-locations=classpath*:mapper/*.xml
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
#mybatis.configLocation=classpath:mybatis-config.xml
@Value("${mybatis.configLocation}")
private String configLocation;
#mybatis.type-aliases-package=com.jht.jscc
@Value("${mybatis.type-aliases-package}")
private String entityPackage;
/**
* 主資料庫
* @return
*/
@Primary
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
/**
* 從資料庫(如有多個按這種方式增加)
* @return
*/
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
/**
* 從資料庫(如有多個按這種方式增加)
* @return
*/
@Bean(name = "otherDataSource")
@ConfigurationProperties(prefix = "druid.jsicp.other")
public DataSource otherDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean(name = "dataSource")
public AbstractRoutingDataSource dataSource(@Qualifier("masterDataSource") DataSource masterDataSource ,
@Qualifier("slaveDataSource") DataSource slaveDataSource,
@Qualifier("otherDataSource") DataSource otherDataSource) {
DynamicRoutingDataSource proxy = new DynamicRoutingDataSource();
Map<Object, Object> targetDataResources = new HashMap<Object, Object>();
targetDataResources.put(DynamicDbContextHolder.DbType.MASTER, masterDataSource);
targetDataResources.put(DynamicDbContextHolder.DbType.SLAVE, slaveDataSource);
targetDataResources.put(DynamicDbContextHolder.DbType.OTHER, otherDataSource);
//設定預設資料來源是master,沒有新增註解的都是預設資料來源
proxy.setDefaultTargetDataSource(masterDataSource);
proxy.setTargetDataSources(targetDataResources);
// 在方法初始化之前執行
proxy.afterPropertiesSet();
return proxy;
}@Bean
// @ConfigurationProperties(prefix = "mybatis")
@Primary
public SqlSessionFactoryBean sqlSessionFactoryBean(@Qualifier("dataSource") DataSource dataSource) throws Exception{
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setConfigLocation(resolver.getResource(configLocation));
sqlSessionFactoryBean.setMapperLocations(resolver.getResources(mapperLocations));
// 設定typeAlias 包掃描路徑
sqlSessionFactoryBean.setTypeAliasesPackage(entityPackage);
return sqlSessionFactoryBean;
}
// 事務管理
@Bean
public PlatformTransactionManager annotationDrivenTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
-----------------------------------------------------------------------------------
//實戰
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.master")
public DataSource masterDataSource(){
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.slave")
public DataSource slaveDataSource(){
return DataSourceBuilder.create().build();
}
@Bean
public DataSource myRoutingDataSource(DataSource masterDataSource, DataSource slaveDataSource){
DynamicRoutingDataSource myRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> targetDataSource = new HashMap<>();
targetDataSource.put(DbTypeEnum.MASTER, masterDataSource);
targetDataSource.put(DbTypeEnum.SLAVE, slaveDataSource);
myRoutingDataSource.setDefaultTargetDataSource(masterDataSource);
myRoutingDataSource.setTargetDataSources(targetDataSource);
return myRoutingDataSource;
}
}
7、mybatis配置
@EnableTransactionManagement
@Configuration
public class MyBatisConfig {
@Resource(name = "myRoutingDataSource")
private DataSource myRoutingDataSource;
@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(myRoutingDataSource);
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(myRoutingDataSource);
}
}
相關文章
- 搭建MySQL主從實現Django讀寫分離MySqlDjango
- Mycat中介軟體實現Mysql主從讀寫分離MySql
- MySQL怎麼實現主從同步和Django實現MySQL讀寫分離MySql主從同步Django
- MYSQL 主從 + ATLAS 讀寫分離 搭建MySql
- 配置\清除 MySQL 主從 讀寫分離MySql
- MySQL主從複製讀寫分離MySql
- MySQL主從分離實現MySql
- MySQL主從複製與讀寫分離MySql
- [Mysql]主從複製和讀寫分離MySql
- Mysql-主從複製與讀寫分離MySql
- ProxySQL實現MySQL讀寫分離MySql
- Amoeba 實現 MySQL 讀寫分離MySql
- 資料庫讀寫分離,主從同步實現方法資料庫主從同步
- mycat結合MySQL的雙主實現讀寫分離MySql
- MySQL主從同步讀寫分離的叢集配置MySql主從同步
- Amoeba+Mysql 實現讀寫分離MySql
- mysql-proxy實現讀寫分離MySql
- MySQL從庫卡主了--讀寫分離也不能亂讀MySql
- MySQL運維15-一主一從讀寫分離MySql運維
- MySQL運維16-雙主雙從讀寫分離MySql運維
- [PHP]Larval主從讀寫分離配置PHP
- 使用proxysql 1.4.14中介軟體實現mysql 5.7.26主從的讀寫分離MySql
- 搭建Redis“主-從-從”模式叢集並使用 RedisTemplate 實現讀寫分離Redis模式
- docker+atlas+mysql實現讀寫分離DockerMySql
- springboot多資料來源配合docker部署mysql主從實現讀寫分離Spring BootDockerMySql
- MySQL 高可用架構:主從備份及讀寫分離MySql架構
- 帶貨直播系統,透過主從同步實現讀寫分離主從同步
- ProxySQL實現Mysql讀寫分離 - 部署手冊MySql
- MySQL 中介軟體Atlas 實現讀寫分離MySql
- Amoeba實現讀寫分離
- MySQL主從複製架構搭建及讀寫分離測試MySql架構
- 搭建Redis簡易叢集實現主從複製和讀寫分離Redis
- Mycat實現mysql的負載均衡讀寫分離MySql負載
- Spring配置RoutingDataSource實現mysql讀寫分離SpringMySql
- mysql proxy 安裝及配置實現讀寫分離MySql
- Kubernetes 中實現 MySQL 的讀寫分離MySql
- Linux下MySQL主從複製(GTID)+讀寫分離(ProxySQL)-實施筆記LinuxMySql筆記
- MySQL Proxy 實現 MySQL 讀寫分離提高併發負載MySql負載