最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離操作

 更新時(shí)間:2024年03月04日 11:02:52   作者:A塵埃  
讀寫(xiě)分離的作用是為了緩解寫(xiě)庫(kù),也就是主庫(kù)的壓力,但一定要基于數(shù)據(jù)一致性的原則,就是保證主從庫(kù)之間的數(shù)據(jù)一定要一致,這篇文章給大家介紹SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離操作,感興趣的朋友跟隨小編一起看看吧

讀寫(xiě)分離的作用是為了緩解寫(xiě)庫(kù),也就是主庫(kù)的壓力,但一定要基于數(shù)據(jù)一致性的原則,就是保證主從庫(kù)之間的數(shù)據(jù)一定要一致。如果一個(gè)方法涉及到寫(xiě)的邏輯,那么該方法里所有的數(shù)據(jù)庫(kù)操作都要走主庫(kù)。

一、環(huán)境部署

  • 數(shù)據(jù)庫(kù):MySql
  • 2個(gè),一主一從
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `user_id` bigint(20) NOT NULL COMMENT '用戶id',
  `user_name` varchar(255) DEFAULT '' COMMENT '用戶名稱',
  `user_phone` varchar(50) DEFAULT '' COMMENT '用戶手機(jī)',
  `address` varchar(255) DEFAULT '' COMMENT '住址',
  `weight` int(3) NOT NULL DEFAULT '1' COMMENT '權(quán)重,大者優(yōu)先',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時(shí)間',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1196978513958141952', '測(cè)試1', '18826334748', '廣州市海珠區(qū)', '1', '2019-11-20 10:28:51', '2019-11-22 14:28:26');
INSERT INTO `user` VALUES ('1196978513958141953', '測(cè)試2', '18826274230', '廣州市天河區(qū)', '2', '2019-11-20 10:29:37', '2019-11-22 14:28:14');
INSERT INTO `user` VALUES ('1196978513958141954', '測(cè)試3', '18826273900', '廣州市天河區(qū)', '1', '2019-11-20 10:30:19', '2019-11-22 14:28:30');

二、依賴

<dependencies>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.16</version>
        </dependency>
        <!-- 動(dòng)態(tài)數(shù)據(jù)源 所需依賴 ### start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- 動(dòng)態(tài)數(shù)據(jù)源 所需依賴 ### end-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.4</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
</dependencies>

三、application.yml配置主從數(shù)據(jù)源

server:
  port: 8001
spring:
  jackson:
      date-format: yyyy-MM-dd HH:mm:ss
      time-zone: GMT+8
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    master:
      url: jdbc:mysql://127.0.0.1:3307/user?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
      username: root
      password:
    slave:
      url: jdbc:mysql://127.0.0.1:3308/user?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&useSSL=false&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
      username: root
      password:

四、Config配置

@Getter
public enum DynamicDataSourceEnum {
    MASTER("master"),
    SLAVE("slave");
    private String dataSourceName;
    DynamicDataSourceEnum(String dataSourceName) {
        this.dataSourceName = dataSourceName;
    }
}
@Configuration
@MapperScan(basePackages = "com.xjt.proxy.mapper", sqlSessionTemplateRef = "sqlTemplate")
public class DataSourceConfig{
	//主庫(kù)
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.master")
	public DataSource masterDb(){
		return DruidDataSourceBuilder.create().build();
	}
	//從庫(kù)
	@Bean
    @ConditionalOnProperty(prefix = "spring.datasource", name = "slave", matchIfMissing = true)
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDb() {
        return DruidDataSourceBuilder.create().build();
    }
	//主從動(dòng)態(tài)配置
	@Bean
	public DynamicDataSource dynamicDb(@Qualifier("masterDb") DataSource masterDataSource,
        @Autowired(required = false) @Qualifier("slaveDb") DataSource slaveDataSource){
		DynamicDataSource dynamicDataSource = new DynamicDataSource();
		Map<Object,Object> targetDataSources = new HashMap<>();
		targetDataSources.put(DynamicDataSourceEnum.MASTER.getDataSourceName(), masterDataSource);
		if(slaveDataSource != null){
			targetDataSources.put(DynamicDataSourceEnum.SLAVE.getDataSourceName(), slaveDataSource);
		}
		dynamicDataSource.setTargetDataSources(targetDataSources);
        dynamicDataSource.setDefaultTargetDataSource(masterDataSource);
        return dynamicDataSource;
	}
	@Bean
	public SqlSessionFactory sessionFactory(){
		SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*Mapper.xml"));
        bean.setDataSource(dynamicDataSource);
        return bean.getObject();
	}
	@Bean
	public SqlSessionTemplate sqlTemplate(){
		return new SqlSessionTemplate(sqlSessionFactory);
	}
	@Bean(name = "dataourceTx")
	public DataSourceTransactionManager dataSourceTx(){
		DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dynamicDataSource);
        return dataSourceTransactionManager;
	}
}

五、設(shè)置路由

為了方便查找對(duì)應(yīng)的數(shù)據(jù)源,我們可以用ThreadLocal保存數(shù)據(jù)源的信息到每個(gè)線程中,方便我們需要時(shí)獲取

pubic class DataSourceContextHolder{
	private static final ThreadLocal<String> DYNAMIC_DATASOURCE_CONTEXT = new ThreadLocal<>();
	public static void set(String datasourceType) {
        DYNAMIC_DATASOURCE_CONTEXT.set(datasourceType);
    }
    public static String get() {
        return DYNAMIC_DATASOURCE_CONTEXT.get();
    }
    public static void clear() {
        DYNAMIC_DATASOURCE_CONTEXT.remove();
    }
}

AbstractRoutingDataSource的作用是基于查找key路由到對(duì)應(yīng)的數(shù)據(jù)源,它內(nèi)部維護(hù)了一組目標(biāo)數(shù)據(jù)源,并且做了路由key與目標(biāo)數(shù)據(jù)源之間的映射,提供基于key查找數(shù)據(jù)源的方法。

public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.get();
    }
}

六、數(shù)據(jù)源的注解

方便切換數(shù)據(jù)源,注解中包含數(shù)據(jù)源對(duì)應(yīng)的枚舉值,默認(rèn)是主庫(kù)

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataSourceSelector {
    DynamicDataSourceEnum value() default DynamicDataSourceEnum.MASTER;
    boolean clear() default true;
}

七、Aop切換數(shù)據(jù)源

定義一個(gè)aop類,對(duì)有注解的方法做切換數(shù)據(jù)源的操作

@Slf4j
@Aspect
@Order(value = 1)
@Component
public class DataSourceContextAop {
 @Around("@annotation(com.xjt.proxy.dynamicdatasource.DataSourceSelector)")
    public Object setDynamicDataSource(ProceedingJoinPoint pjp) throws Throwable {
        boolean clear = true;
        try {
            Method method = this.getMethod(pjp);
            DataSourceSelector dataSourceImport = method.getAnnotation(DataSourceSelector.class);//獲取注解標(biāo)注的方法
            clear = dataSourceImport.clear();
            DataSourceContextHolder.set(dataSourceImport.value().getDataSourceName());
            log.info("========數(shù)據(jù)源切換至:{}", dataSourceImport.value().getDataSourceName());
            return pjp.proceed();
        } finally {
            if (clear) {
                DataSourceContextHolder.clear();
            }
        }
    }
    private Method getMethod(JoinPoint pjp) {
        MethodSignature signature = (MethodSignature)pjp.getSignature();
        return signature.getMethod();
    }
}

八、測(cè)試

寫(xiě)好Service文件,包含讀取和更新兩個(gè)方法

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    @DataSourceSelector(value = DynamicDataSourceEnum.SLAVE)
    public List<User> listUser() {
        List<User> users = userMapper.selectAll();
        return users;
    }
    @DataSourceSelector(value = DynamicDataSourceEnum.MASTER)
    public int update() {
        User user = new User();
        user.setUserId(Long.parseLong("1196978513958141952"));
        user.setUserName("修改后的名字2");
        return userMapper.updateByPrimaryKeySelective(user);
    }
    @DataSourceSelector(value = DynamicDataSourceEnum.SLAVE)
    public User find() {
        User user = new User();
        user.setUserId(Long.parseLong("1196978513958141952"));
        return userMapper.selectByPrimaryKey(user);
    }
}

根據(jù)方法上的注解可以看出,讀的方法走從庫(kù),更新的方法走主庫(kù),更新的對(duì)象是userId為1196978513958141953 的數(shù)據(jù)

@RunWith(SpringRunner.class)
@SpringBootTest
class UserServiceTest {
    @Autowired
    UserService userService;
    @Test
    void listUser() {
        List<User> users = userService.listUser();
        for (User user : users) {
            System.out.println(user.getUserId());
            System.out.println(user.getUserName());
            System.out.println(user.getUserPhone());
        }
    }
    @Test
    void update() {
        userService.update();
        User user = userService.find();
        System.out.println(user.getUserName());
    }
}

讀取方法

在這里插入圖片描述

更新方法

在這里插入圖片描述

執(zhí)行之后,比對(duì)數(shù)據(jù)庫(kù)就可以發(fā)現(xiàn)主從庫(kù)都修改了數(shù)據(jù),說(shuō)明我們的讀寫(xiě)分離是成功的。當(dāng)然,更新方法可以指向從庫(kù),這樣一來(lái)就只會(huì)修改到從庫(kù)的數(shù)據(jù),而不會(huì)涉及到主庫(kù)。

到此這篇關(guān)于SpringBoot+aop實(shí)現(xiàn)主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離的文章就介紹到這了,更多相關(guān)SpringBoot主從數(shù)據(jù)庫(kù)的讀寫(xiě)分離內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java JDK1.5、1.6、1.7新特性整理

    Java JDK1.5、1.6、1.7新特性整理

    這篇文章主要介紹了Java JDK1.5、1.6、1.7新特性整理,需要的朋友可以參考下
    2016-10-10
  • 解決java調(diào)用dll報(bào)Unable to load library錯(cuò)誤的問(wèn)題

    解決java調(diào)用dll報(bào)Unable to load library錯(cuò)誤的問(wèn)題

    這篇文章主要介紹了解決java調(diào)用dll報(bào)Unable to load library錯(cuò)誤的問(wèn)題。具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-11-11
  • Java棧和基礎(chǔ)隊(duì)列的實(shí)現(xiàn)詳解

    Java棧和基礎(chǔ)隊(duì)列的實(shí)現(xiàn)詳解

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)中的棧與隊(duì)列,在Java的時(shí)候,對(duì)于棧與隊(duì)列的應(yīng)用需要熟練的掌握,這樣才能夠確保Java學(xué)習(xí)時(shí)候能夠有扎實(shí)的基礎(chǔ)能力。本文小編就來(lái)詳細(xì)說(shuō)說(shuō)Java中的棧與隊(duì)列,需要的朋友可以參考一下
    2022-02-02
  • springboot Actuator的指標(biāo)監(jiān)控可視化功能詳解

    springboot Actuator的指標(biāo)監(jiān)控可視化功能詳解

    SpringBoot Actuator是springboot為簡(jiǎn)化我們對(duì)微服務(wù)項(xiàng)目的監(jiān)控功能抽取出來(lái)的模塊,使得我們每個(gè)微服務(wù)快速引用即可獲得生產(chǎn)界別的應(yīng)用監(jiān)控、審計(jì)等功能。這篇文章主要介紹了springboot Actuator的指標(biāo)監(jiān)控可視化,需要的朋友可以參考下
    2021-08-08
  • mybatis-plus之如何根據(jù)數(shù)據(jù)庫(kù)主鍵定義字段類型

    mybatis-plus之如何根據(jù)數(shù)據(jù)庫(kù)主鍵定義字段類型

    這篇文章主要介紹了mybatis-plus之如何根據(jù)數(shù)據(jù)庫(kù)主鍵定義字段類型問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 一篇文章徹底搞懂面試中常被問(wèn)的各種“鎖”

    一篇文章徹底搞懂面試中常被問(wèn)的各種“鎖”

    這篇文章主要給大家介紹了關(guān)于面試中常被問(wèn)的各種“鎖”的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存的實(shí)例代碼

    spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存的實(shí)例代碼

    這篇文章主要介紹了spring整合redis實(shí)現(xiàn)數(shù)據(jù)緩存,需要的朋友可以參考下
    2018-09-09
  • idea上提交項(xiàng)目到gitee 最后出現(xiàn) Push rejected的問(wèn)題處理方法

    idea上提交項(xiàng)目到gitee 最后出現(xiàn) Push rejected的問(wèn)題處理方法

    這篇文章主要介紹了idea上面提交項(xiàng)目到gitee 最后出現(xiàn) Push rejected的問(wèn)題處理方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java參數(shù)校驗(yàn)中validation和validator的區(qū)別詳解

    Java參數(shù)校驗(yàn)中validation和validator的區(qū)別詳解

    這篇文章主要介紹了Java參數(shù)校驗(yàn)中validation和validator的區(qū)別詳解,一般對(duì)于復(fù)雜的業(yè)務(wù)參數(shù)校驗(yàn),可以通過(guò)校驗(yàn)類單獨(dú)的校驗(yàn)方法進(jìn)行處理,通常對(duì)于一些與業(yè)務(wù)無(wú)關(guān)簡(jiǎn)單的參數(shù)校驗(yàn)可以采用validation和 validator通過(guò)注解的方式實(shí)現(xiàn)校驗(yàn),需要的朋友可以參考下
    2023-10-10
  • Java?List一些常用方法完全攻略

    Java?List一些常用方法完全攻略

    List就是一個(gè)線性表,即n個(gè)具有相同類型元素的有限序列,在該序列上可以執(zhí)行增刪改查以及變量等操作,這篇文章主要介紹了Java List一些常用方法的相關(guān)資料,需要的朋友可以參考下
    2026-04-04

最新評(píng)論

汪清县| 阜康市| 盐城市| 师宗县| 乡宁县| 定日县| 南涧| 高青县| 定襄县| 岑巩县| 泰宁县| 潮州市| 阿图什市| 曲水县| 宜兰市| 扬中市| 青川县| 嘉鱼县| 岚皋县| 抚顺市| 元朗区| 南昌县| 沅陵县| 靖安县| 宜城市| 临安市| 准格尔旗| 永和县| 桐梓县| 陆丰市| 宁安市| 岐山县| 盖州市| 合山市| 石台县| 右玉县| 克拉玛依市| 额尔古纳市| 丹棱县| 岱山县| 竹北市|