日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

springboot 多数据源 读写分离 AOP方式

發布時間:2023/12/10 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot 多数据源 读写分离 AOP方式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

大家好,我是烤鴨:

? ? ? ? 今天分享springboot讀寫分離配置。

? ? ? ? ?環境:

? ? ? ? ? ? ? ? ?springboot ?2.1.0.RELEASE

? ? ? ? ?場景說明,目前的需求是 讀數據源 * 2 + 寫數據源 * 1

?

1.? ? 配置文件


?? ?application.yml

server:port: 8085 spring:application:name: test-data-testdatasource:write:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test.Devdriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readaw:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100readdc:jdbc-url: jdbc:mysql://localhost:3306/testusername: rootpassword: test!idriver-class-name: com.mysql.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSource connectionTimeout: 30000validationTimeout: 5000maxPoolSize: 200minIdle: 100 #mybatis mybatis:###把xml文件放在com.XX.mapper.*中可能會出現找到的問題,這里把他放在resource下的mapper中mapper-mapperLocations: classpath*:mapper/**/**/*.xmltype-aliases-package: com.test.test.pojoconfiguration:map-underscore-to-camel-case: truecache-enabled: falsecall-setters-on-nulls: trueuseGeneratedKeys: true

2.? ? 配置類


?DataSourceConfig.java

?默認 讀數據源,如果需要增加或者減少數據源需要修改 myRoutingDataSource 方法中的參數

package com.test.test.config.db;import com.test.test.datasource.MyRoutingDataSource; import com.test.test.datasource.enums.DBTypeEnum; import com.zaxxer.hikari.HikariDataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment;import javax.sql.DataSource; import java.util.HashMap; import java.util.Map;/*** 關于數據源配置,參考SpringBoot官方文檔第79章《Data Access》* 79. Data Access* 79.1 Configure a Custom DbSource* 79.2 Configure Two DataSources*/@Configuration public class DataSourceConfig {@AutowiredEnvironment environment;@Bean@ConfigurationProperties("spring.datasource.readaw")public DataSource readDataSourceAw() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readaw");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.readdc")public DataSource readDataSourceDc() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"readdc");return hikariDataSource;}@Bean@ConfigurationProperties("spring.datasource.write")public DataSource writeDataSource() {DataSource build = DataSourceBuilder.create().build();HikariDataSource hikariDataSource = buildDataSource(build,"write");return hikariDataSource;}@Beanpublic DataSource myRoutingDataSource(@Qualifier("readDataSourceAw") DataSource readDataSourceAw,@Qualifier("readDataSourceDc") DataSource readDataSourceDc,@Qualifier("writeDataSource") DataSource writeDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DBTypeEnum.READ_AW, readDataSourceAw);targetDataSources.put(DBTypeEnum.READ_DC, readDataSourceDc);targetDataSources.put(DBTypeEnum.WRITE, writeDataSource);MyRoutingDataSource myRoutingDataSource = new MyRoutingDataSource();myRoutingDataSource.setDefaultTargetDataSource(readDataSourceAw);myRoutingDataSource.setTargetDataSources(targetDataSources);return myRoutingDataSource;}public HikariDataSource buildDataSource(DataSource dataSource,String dataSourcePrefix){HikariDataSource hikariDataSource= (HikariDataSource) dataSource;hikariDataSource.setDriverClassName(environment.getProperty("spring.datasource."+dataSourcePrefix+".driver-class-name"));hikariDataSource.setJdbcUrl(environment.getProperty("spring.datasource."+dataSourcePrefix+".jdbc-url"));hikariDataSource.setUsername(environment.getProperty("spring.datasource."+dataSourcePrefix+".username"));hikariDataSource.setPassword(environment.getProperty("spring.datasource."+dataSourcePrefix+".password"));hikariDataSource.setMinimumIdle(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".minIdle")));hikariDataSource.setConnectionTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".connectionTimeout")));hikariDataSource.setValidationTimeout(Long.parseLong(environment.getProperty("spring.datasource."+dataSourcePrefix+".validationTimeout")));hikariDataSource.setMaximumPoolSize(Integer.parseInt(environment.getProperty("spring.datasource."+dataSourcePrefix+".maxPoolSize")));return hikariDataSource;} }

MyBatisConfig.java

注意映射mapper文件路徑是在這里修改的,因為重新注入了sqlSession, yml中配置的無效

package com.test.test.config.mybatis;import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.annotation.Resource; import javax.sql.DataSource;@EnableTransactionManagement @Configuration public class MyBatisConfig {@Resource(name = "myRoutingDataSource")private DataSource myRoutingDataSource;@Beanpublic SqlSessionFactory sqlSessionFactory() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(myRoutingDataSource);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/**/*.xml"));return sqlSessionFactoryBean.getObject();}@Beanpublic PlatformTransactionManager platformTransactionManager() {return new DataSourceTransactionManager(myRoutingDataSource);} }

DataSourceAop.java

aop配置類,通過aop的方式限制哪個service的方法連接哪個數據源
目前是根據類上的注解來判斷,可以修改為根據方法的注解來判斷走哪個數據源

package com.test.test.datasource.aop;import com.test.test.datasource.annotation.DbSource; import com.test.test.datasource.handler.DBContextHolder; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect @Component public class DataSourceAop {/*** 另一種寫法:if...else... 判斷哪些需要讀從數據庫,其余的走主數據庫*/@Before("execution(* com.test.test.service.impl.*.*(..))")public void before(JoinPoint jp){MethodSignature methodSignature = (MethodSignature) jp.getSignature();Method method = methodSignature.getMethod();System.out.println("攔截到了" + jp.getSignature().getName() +"方法...");Class<?> targetClass = jp.getTarget().getClass();boolean flag = targetClass.isAnnotationPresent(DbSource.class);//包含數據源注解,數據源為注解中的類if(flag){//獲取注解的valueDbSource annotation = targetClass.getAnnotation(DbSource.class);String value = annotation.value();DBContextHolder.read(value);}else {//不包含注解,查詢方法默認走 默認讀數據源if (StringUtils.startsWithAny(method.getName(), "get", "select", "find")) {DBContextHolder.read("");}else {DBContextHolder.write();}}} }

DBTypeEnum.java

數據源枚舉,增加和減少數據源修改即可

public enum DBTypeEnum {READ_AW, READ_DC, WRITE;}

DBContextHolder.java

數據源切換類,保持當前線程綁定哪個數據源

package com.test.test.datasource.handler;import com.test.test.datasource.enums.DBTypeEnum; import org.apache.commons.lang3.StringUtils;import java.util.concurrent.atomic.AtomicInteger; /*** @Author gmwang* @Description // 數據源切換類* @Date 2019/4/30 9:20* @Param* @return**/ public class DBContextHolder {private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();private static final AtomicInteger counter = new AtomicInteger(-1);public static void set(DBTypeEnum dbType) {contextHolder.set(dbType);}public static DBTypeEnum get() {return contextHolder.get();}public static void read(String value) {if(StringUtils.isBlank(value)){set(DBTypeEnum.READ_AW);System.out.println("切換到讀"+DBTypeEnum.READ_AW.toString());}if (DBTypeEnum.READ_DC.toString().equals(value)){set(DBTypeEnum.READ_DC);System.out.println("切換到讀"+DBTypeEnum.READ_DC.toString());}}public static void write() {set(DBTypeEnum.WRITE);System.out.println("切換到寫"+DBTypeEnum.WRITE.toString());} }

MyRoutingDataSource.java

多數據源的路由類

package com.test.test.datasource;import com.test.test.datasource.handler.DBContextHolder; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.lang.Nullable; /*** @Author gmwang* @Description //多數據源的路由* @Date 2019/4/30 9:38* @Param* @return**/ public class MyRoutingDataSource extends AbstractRoutingDataSource {/*** @Author gmwang* @Description //根據Key獲取數據源的信息,上層抽象函數的鉤子* @Date 2019/4/30 9:39* @Param []* @return java.lang.Object**/@Nullable@Overrideprotected Object determineCurrentLookupKey() {return DBContextHolder.get();} }

DbSource

數據源注解,加在serivice實現類上,指定 value,AOP根據注解獲取指定的數據源。

package com.test.test.datasource.annotation;import java.lang.annotation.*;@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DbSource {String value(); }

例如本例中,默認讀庫是? READ_AW ,如果不加注解默認,讀取默認庫。如果指定注解 READ_DC,就用指定的數據源。

?

3.? ? 結果測試

偽代碼:

?

在tes方法中使用查詢(不同的庫)后插入操作,結果如圖所示。

?

總結

以上是生活随笔為你收集整理的springboot 多数据源 读写分离 AOP方式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。