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

基于Mybatis實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的示例代碼

 更新時間:2024年09月20日 08:32:19   作者:WheelMouse7788  
在當(dāng)今的互聯(lián)網(wǎng)應(yīng)用中,微服務(wù)大行其道,隨著業(yè)務(wù)的發(fā)展和擴(kuò)展,單一的數(shù)據(jù)庫無法滿足日益增長的數(shù)據(jù)需求,本文將基于 JDK17 + Spring Boot 3 和 MyBatis 框架實(shí)現(xiàn)動態(tài)切換數(shù)據(jù)源功能,需要的朋友可以參考下

引言

在當(dāng)今的互聯(lián)網(wǎng)應(yīng)用中,微服務(wù)大行其道,隨著業(yè)務(wù)的發(fā)展和擴(kuò)展,單一的數(shù)據(jù)庫無法滿足日益增長的數(shù)據(jù)需求,一個業(yè)務(wù)接口可能需要查詢多個數(shù)據(jù)源的數(shù)據(jù)組裝到一起返回給頁面進(jìn)行呈現(xiàn),此時就需要考慮使用動態(tài)數(shù)據(jù)源技術(shù)。 本文將基于 JDK17 + Spring Boot 3 和 MyBatis 框架實(shí)現(xiàn)動態(tài)切換數(shù)據(jù)源功能。

代碼開發(fā)

  • pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.learning</groupId>
	<artifactId>learning-mybatis</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>learning-mybatis</name>
	<description>learning-mybatis</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.3</version>
		</dependency>

		<dependency>
			<groupId>com.mysql</groupId>
			<artifactId>mysql-connector-j</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

  • 啟動類 Application.java
@MapperScan("com.learning.**.mapper")
@SpringBootApplication(scanBasePackages = {"com.learning"})
public class LearningMybatisApplication {

	public static void main(String[] args) {
		SpringApplication.run(LearningMybatisApplication.class, args);
	}

}
  • Controller
@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    StudentService studentService;

    @GetMapping("/selectAll")
    public String selectAll() {
        List<Student> students = studentService.selectAll();
        for (Student student : students) {
            System.out.println(student);
        }
        return "success";
    }

}
  • Service 和 Impl
public interface IStudentService {

    List<Student> selectAll();

}

@Service
public class StudentServiceImpl implements IStudentService {

    @Autowired
    StudentMapper mapper;

    @Override
    public List<Student> selectAll() {
        List<Student> students = mapper.selectAll();
        List<Student> dataSource2All = mapper.selectDataSource2All();
        return mapper.selectAll();
    }
}
  • Mapper
public interface StudentMapper {

    @DynamicDataSource("dataSource1")
    List<Student> selectAll();

    @DynamicDataSource("dataSource2")
    List<Student> selectDataSource2All();

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.learning.mybatis.mapper.StudentMapper">

    <resultMap id="BaseResultMap" type="com.learning.mybatis.entities.Student">
            <id property="id" column="id" jdbcType="VARCHAR"/>
            <result property="name" column="name" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="Base_Column_List">
        id,name,age
    </sql>

    <select id="selectAll" resultMap="BaseResultMap">
        select
            id,name,age
        from student
    </select>

    <select id="selectDataSource2All" resultMap="BaseResultMap">
        select
            id,name
        from tx
    </select>
</mapper>

  • 配置文件
spring.application.name=learning-mybatis

# ======================== Mybatis ===============
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

動態(tài)數(shù)據(jù)源切換 AOP 實(shí)現(xiàn)

  • 首先,我們需要實(shí)現(xiàn)AbstractRoutingDataSource 接口的determineCurrentLookupKey()方法
public class DynamicDataSourceRouter extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceType();
    }
}
  • 創(chuàng)建一個數(shù)據(jù)源上下文持有者,用于保存和獲取當(dāng)前線程的數(shù)據(jù)源。
public class DynamicDataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new InheritableThreadLocal<>();

    public static void setDataSourceType(String dataSourceType) {
        contextHolder.set(dataSourceType);
    }

    public static String getDataSourceType() {
        return contextHolder.get();
    }

    public static void clearDataSourceType() {
        contextHolder.remove();
    }

}
  • 配置你的數(shù)據(jù)源。這里假設(shè)你已經(jīng)有兩個數(shù)據(jù)源dataSource1dataSource2
@Configuration
public class DataSourceConfig {

    @Bean
    public DataSource dataSource() {
        DynamicDataSourceRouter routingDataSource = new DynamicDataSourceRouter();
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put("dataSource1", dataSource1()); // 你的第一個數(shù)據(jù)源
        dataSourceMap.put("dataSource2", dataSource2()); // 你的第二個數(shù)據(jù)源
        routingDataSource.setTargetDataSources(dataSourceMap);
        routingDataSource.setDefaultTargetDataSource(dataSource1()); // 默認(rèn)數(shù)據(jù)源

        return routingDataSource;
    }

    private DataSource dataSource1() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db2024?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        // 設(shè)置其他HikariDataSource的屬性,如連接池大小等
        return dataSource;
    }

    private DataSource dataSource2() {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/tx2021?characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        // 設(shè)置其他HikariDataSource的屬性,如連接池大小等
        return dataSource;
    }


}
  • 定義注解,用于標(biāo)記需要切換數(shù)據(jù)源的方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicDataSource {

    String value(); // 數(shù)據(jù)源名稱

}
  • 定義切面,使用AOP來攔截帶有@DynamicDataSource注解的方法,并在方法執(zhí)行前后切換數(shù)據(jù)源
@Aspect
@Order(-1) // 確保該AOP在事務(wù)AOP之前執(zhí)行
@Component
public class DynamicDataSourceAspect {

    @Before("@annotation(dynamicDataSource)")
    public void switchDataSource(JoinPoint point, DynamicDataSource dynamicDataSource) {
        DynamicDataSourceContextHolder.setDataSourceType(dynamicDataSource.value());
    }

    @After("@annotation(dynamicDataSource)")
    public void restoreDataSource(JoinPoint point, DynamicDataSource dynamicDataSource) {
        DynamicDataSourceContextHolder.clearDataSourceType();
    }


}

至此,完活,拿去測試看效果。

動態(tài)切換數(shù)據(jù)源實(shí)現(xiàn)原理分析

核心代碼:AbstractRoutingDataSource AbstractRoutingDataSource 是 Spring 框架提供的一個抽象類,它實(shí)現(xiàn)了 DataSource 接口,內(nèi)部維護(hù)了一個用來存儲數(shù)據(jù)源和它們對應(yīng)的 key的Map,這個 Map 是在構(gòu)造函數(shù)或者配置方法(如 setTargetDataSources)中設(shè)置的。

// org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
@Nullable
private Map<Object, Object> targetDataSources;

public void setTargetDataSources(Map<Object, Object> targetDataSources) {
    this.targetDataSources = targetDataSources;
}

determineCurrentLookupKey() 方法是 AbstractRoutingDataSource 的核心。它是一個抽象方法,子類必須實(shí)現(xiàn)它來提供當(dāng)前的 key。

// org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource
@Nullable
protected abstract Object determineCurrentLookupKey();

當(dāng) AbstractRoutingDataSource 的 getConnection() 方法被調(diào)用時,它會調(diào)用 determineCurrentLookupKey() 來獲取當(dāng)前的數(shù)據(jù)源 key,然后使用這個 key 從 Map 中獲取對應(yīng)的數(shù)據(jù)源。

// org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

public Connection getConnection() throws SQLException {
    return this.determineTargetDataSource().getConnection();
}

protected DataSource determineTargetDataSource() {
    Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
    Object lookupKey = this.determineCurrentLookupKey();
    DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
    if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
        dataSource = this.resolvedDefaultDataSource;
    }

    if (dataSource == null) {
        throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
    } else {
        return dataSource;
    }
}

一旦 determineTargetDataSource() 方法返回了合適的數(shù)據(jù)源,AbstractRoutingDataSource 就會使用這個數(shù)據(jù)源來獲取數(shù)據(jù)庫連接。 由于 determineCurrentLookupKey() 方法在每個數(shù)據(jù)庫操作之前都會被調(diào)用,所以只要在適當(dāng)?shù)牡胤叫薷?determineCurrentLookupKey() 的實(shí)現(xiàn),就可以實(shí)現(xiàn)在不同的數(shù)據(jù)庫操作間切換數(shù)據(jù)源。

總結(jié)

通過實(shí)現(xiàn) AbstractRoutingDataSource.determineCurrentLookupKey() 方法,并結(jié)合 Spring 框架內(nèi)部的 AbstractRoutingDataSource 邏輯,我們可以實(shí)現(xiàn)在運(yùn)行時根據(jù)不同的條件動態(tài)地選擇和切換數(shù)據(jù)源。這種機(jī)制允許應(yīng)用程序在處理不同的請求或事務(wù)時使用不同的數(shù)據(jù)庫連接,從而提供了極大的靈活性和擴(kuò)展性。

以上就是基于Mybatis實(shí)現(xiàn)動態(tài)數(shù)據(jù)源切換的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Mybatis動態(tài)數(shù)據(jù)源切換的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • SpringBoot集成JWT實(shí)現(xiàn)Token登錄驗(yàn)證的示例代碼

    SpringBoot集成JWT實(shí)現(xiàn)Token登錄驗(yàn)證的示例代碼

    隨著技術(shù)的發(fā)展,分布式web應(yīng)用的普及,通過session管理用戶登錄狀態(tài)成本越來越高,因此慢慢發(fā)展成為token的方式做登錄身份校驗(yàn),本文就來介紹一下SpringBoot集成JWT實(shí)現(xiàn)Token登錄驗(yàn)證的示例代碼,感興趣的可以了解一下
    2023-12-12
  • java實(shí)現(xiàn)jdbc查詢結(jié)果集result轉(zhuǎn)換成對應(yīng)list集合

    java實(shí)現(xiàn)jdbc查詢結(jié)果集result轉(zhuǎn)換成對應(yīng)list集合

    本文給大家匯總介紹了java實(shí)現(xiàn)jdbc查詢結(jié)果集result轉(zhuǎn)換成對應(yīng)list集合,十分的簡單,有相同需求的小伙伴可以參考下。
    2015-12-12
  • ChatGPT介紹及Java?API調(diào)用

    ChatGPT介紹及Java?API調(diào)用

    本文主要介紹了ChatGPT介紹及Java?API調(diào)用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • java swing框架實(shí)現(xiàn)貪吃蛇游戲

    java swing框架實(shí)現(xiàn)貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了java swing框架實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • lombok的@EqualsAndHashcode注解詳解

    lombok的@EqualsAndHashcode注解詳解

    這篇文章主要介紹了lombok的@EqualsAndHashcode注解的用法,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java導(dǎo)出Execl疑難點(diǎn)處理的實(shí)現(xiàn)

    Java導(dǎo)出Execl疑難點(diǎn)處理的實(shí)現(xiàn)

    這篇文章主要介紹了Java導(dǎo)出Execl疑難點(diǎn)處理的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04
  • Intellij idea遠(yuǎn)程debug連接tomcat實(shí)現(xiàn)單步調(diào)試

    Intellij idea遠(yuǎn)程debug連接tomcat實(shí)現(xiàn)單步調(diào)試

    這篇文章主要介紹了Intellij idea遠(yuǎn)程debug連接tomcat實(shí)現(xiàn)單步調(diào)試,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • java如何不通過構(gòu)造函數(shù)創(chuàng)建對象(Unsafe)

    java如何不通過構(gòu)造函數(shù)創(chuàng)建對象(Unsafe)

    這篇文章主要介紹了java如何不通過構(gòu)造函數(shù)創(chuàng)建對象(Unsafe)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • Maven打包跳過測試的實(shí)現(xiàn)方法

    Maven打包跳過測試的實(shí)現(xiàn)方法

    使用Maven打包的時候,可能會因?yàn)閱卧獪y試打包失敗,這時候就需要跳過單元測試。本文就介紹了Maven打包跳過測試的實(shí)現(xiàn)方法,感興趣的可以了解一下
    2021-06-06
  • Java多線程之CAS算法實(shí)現(xiàn)線程安全

    Java多線程之CAS算法實(shí)現(xiàn)線程安全

    這篇文章主要介紹了java中如何通過CAS算法實(shí)現(xiàn)線程安全,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,下面小編和大家一起來學(xué)習(xí)一下吧
    2019-05-05

最新評論