欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

一、環(huán)境部署

  • 數(shù)據(jù)庫:MySql
  • 2個,一主一從
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 '用戶手機',
  `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)建時間',
  `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間',
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `user` VALUES ('1196978513958141952', '測試1', '18826334748', '廣州市海珠區(qū)', '1', '2019-11-20 10:28:51', '2019-11-22 14:28:26');
INSERT INTO `user` VALUES ('1196978513958141953', '測試2', '18826274230', '廣州市天河區(qū)', '2', '2019-11-20 10:29:37', '2019-11-22 14:28:14');
INSERT INTO `user` VALUES ('1196978513958141954', '測試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>
        <!-- 動態(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>
        <!-- 動態(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{
	//主庫
	@Bean
	@ConfigurationProperties(prefix = "spring.datasource.master")
	public DataSource masterDb(){
		return DruidDataSourceBuilder.create().build();
	}
	//從庫
	@Bean
    @ConditionalOnProperty(prefix = "spring.datasource", name = "slave", matchIfMissing = true)
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slaveDb() {
        return DruidDataSourceBuilder.create().build();
    }
	//主從動態(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è)置路由

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

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路由到對應(yīng)的數(shù)據(jù)源,它內(nèi)部維護了一組目標數(shù)據(jù)源,并且做了路由key與目標數(shù)據(jù)源之間的映射,提供基于key查找數(shù)據(jù)源的方法。

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

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

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

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

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

定義一個aop類,對有注解的方法做切換數(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);//獲取注解標注的方法
            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();
    }
}

八、測試

寫好Service文件,包含讀取和更新兩個方法

@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ù)方法上的注解可以看出,讀的方法走從庫,更新的方法走主庫,更新的對象是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í)行之后,比對數(shù)據(jù)庫就可以發(fā)現(xiàn)主從庫都修改了數(shù)據(jù),說明我們的讀寫分離是成功的。當然,更新方法可以指向從庫,這樣一來就只會修改到從庫的數(shù)據(jù),而不會涉及到主庫。

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

相關(guān)文章

  • Java(enum)枚舉用法詳解

    Java(enum)枚舉用法詳解

    本篇文章主要介紹了Java 枚舉用法詳解,枚舉的好處:可以將常量組織起來,統(tǒng)一進行管理。有興趣的可以一起來了解一下。
    2016-11-11
  • 深入探究Java中的HashMap為什么會產(chǎn)生死循環(huán)

    深入探究Java中的HashMap為什么會產(chǎn)生死循環(huán)

    HashMap?死循環(huán)發(fā)生在?JDK?1.8?之前的版本中,這篇文章主要來和大家深入探究一下為什么Java中HashMap會產(chǎn)生死循環(huán),感興趣的小伙伴可以了解一下
    2023-05-05
  • java理論基礎(chǔ)Stream性能論證測試示例

    java理論基礎(chǔ)Stream性能論證測試示例

    這篇文章主要為大家介紹了java理論基礎(chǔ)Stream性能論證的測試示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步
    2022-03-03
  • 6種SpringBoot中自定義starter的方式介紹

    6種SpringBoot中自定義starter的方式介紹

    在SpringBoot生態(tài)中,starter是一種特殊的依賴,它能夠自動裝配相關(guān)組件,簡化項目配置,本文將詳細介紹6種不同的自定義starter開發(fā)方法,有需要的可以了解下
    2025-04-04
  • 淺談springboot項目中定時任務(wù)如何優(yōu)雅退出

    淺談springboot項目中定時任務(wù)如何優(yōu)雅退出

    這篇文章主要介紹了淺談springboot項目中定時任務(wù)如何優(yōu)雅退出?具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 詳解Java線程中常用操作

    詳解Java線程中常用操作

    這篇文章主要為大家詳細介紹了一下Java線程中的一些常用操作,文中的示例代碼講解詳細,對我們學習或工作有一定幫助,需要的可以參考一下
    2022-05-05
  • Mybatis-Plus中的@TableName 和 table-prefix使用

    Mybatis-Plus中的@TableName 和 table-prefix使用

    table-prefix 是一個全局配置,它會自動在所有表名前添加指定的前綴,這個配置對于那些使用一致命名約定的數(shù)據(jù)庫表非常有用,這篇文章主要介紹了Mybatis-Plus中的@TableName 和 table-prefix使用,需要的朋友可以參考下
    2024-08-08
  • Java 8新的時間日期庫的20個使用示例

    Java 8新的時間日期庫的20個使用示例

    這篇文章主要介紹了Java 8新的時間日期庫的20個使用示例,需要的朋友可以參考下
    2015-04-04
  • 淺談HashMap在高并發(fā)下的問題

    淺談HashMap在高并發(fā)下的問題

    這篇文章主要介紹了HashMap在高并發(fā)下的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • Spring AI ectorStore的使用流程

    Spring AI ectorStore的使用流程

    SpringAI中的VectorStore是一種用于存儲和檢索高維向量數(shù)據(jù)的數(shù)據(jù)庫或存儲解決方案,它在AI應(yīng)用中發(fā)揮著至關(guān)重要的作用,本文給大家介紹Spring AI ectorStore的使用流程,感興趣的朋友一起看看吧
    2025-03-03

最新評論