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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Boot2.x-09 基于Spring Boot 2.1.2 + Mybatis使用自定义注解实现数据库切换

發布時間:2025/3/21 javascript 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Boot2.x-09 基于Spring Boot 2.1.2 + Mybatis使用自定义注解实现数据库切换 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 概述
  • 場景說明:讀寫分離
  • 操作步驟
    • 工程結構
    • Step1 自定義注解
    • Step2 數據源定義
    • Step3 配置文件配置數據源
    • Step4 數據源實例化DatasourceConfig
    • Step5 Mybatis中配置成動態數據源
    • Step6 ThreadLocal管理當前線程使用的數據源連接
    • Step7 切面
    • Step 8 核心方法,重寫AbstractRoutingDataSource#determineCurrentLookupKey
  • 測試
    • 庫表數據
    • Domain
    • Dao
    • Service
    • Controller
    • 啟動Spring Boot 工程
  • 代碼

概述

之前總結過一篇基于Spring的 數據庫切換的文章:Spring-基于Spring使用自定義注解及Aspect實現數據庫切換 ,新的項目一般都直接采用SpringBoot開發了,那我們也用Spring Boot來整一版吧。

用到的東西包含: Spring Boot + Mybatis + Druid + MySql8 + lombok 等

鑒于我們是整合了Spring Boot +Mybatis , 不清楚如何整合的可以先看下
Spring Boot2.x-07Spring Boot2.1.2整合Mybatis


場景說明:讀寫分離

簡單說下適用場景【讀寫分離】:數據庫切換通常情況是用在項目中存在主從數據庫的情況,為了減輕主庫的壓力,因為主從是同步的,所以讀的操作我們直接取從庫的數據,主庫只負責寫的操作。從庫可以使多個,當然了主庫也可以是多個,看項目架構。 這個同多數據源還是有差別的,如何支持多數據源,后面單獨開篇介紹下。

廢話不多說,直接擼起來吧


操作步驟

核心還是重寫Spring的AbstractRoutingDataSource抽象類的determineCurrentLookupKey方法。


工程結構


Step1 自定義注解

這里我們先約定,自定義注解只能標注在方法上,如果需要也能標注在類上(因為后面的判斷會有Aspect判斷會所不同)請參考 Spring-基于Spring使用自定義注解及Aspect實現數據庫切換

package com.artisan.annotation;import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.RetentionPolicy.RUNTIME;import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target;import com.artisan.config.DataSources;/*** * 自定義注解,用于切換數據源,默認MASTER_DB* @author yangshangwei**/@Documented @Retention(RUNTIME) @Target({ METHOD }) public @interface RouteDataSource {String value() default DataSources.MASTER_DB;}

Step2 數據源定義

為了方便能夠注解引用,直接用接口吧

package com.artisan.config;/*** 數據源列表* @author yangshangwei**/ public interface DataSources {String MASTER_DB = "masterDB";String SLAVE_DB = "slaveDB";}

Step3 配置文件配置數據源

我們這里采用application.yml ,注意前綴,后面要用。

# datasource Master 前綴為自定義的datasource-master spring:datasource-master:driver-class-name: com.mysql.cj.jdbc.Driver # JDBC連接Mysql6以上com.mysql.cj.jdbc.Driver (服務端為Mysql8)url: jdbc:mysql://localhost:3306/master?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: root# datasource Replication 前綴為自定義的datasource-slavedatasource-slave: driver-class-name: com.mysql.cj.jdbc.Driver # JDBC連接Mysql6以上com.mysql.cj.jdbc.Driver (服務端為Mysql8)url: jdbc:mysql://localhost:3306/slave?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: root

Step4 數據源實例化DatasourceConfig

通過@Configuration標注為配置類。被注解的類內部包含有一個或多個被@Bean注解的方法,這些方法將會被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext類進行掃描,并用于構建bean定義,初始化Spring容器。

application.yml中定義的前綴,別搞錯了。

package com.artisan.config;import javax.sql.DataSource;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 com.alibaba.druid.pool.DruidDataSource;@Configuration public class DatasourceConfig {//destroy-method="close":當數據庫連接不使用的時候,將該連接重新放到數據池中@Bean(name=DataSources.MASTER_DB,destroyMethod="close")@ConfigurationProperties(prefix = "spring.datasource-master")public DataSource dataSource() {// 創建數據源return DataSourceBuilder.create().type(DruidDataSource.class).build();}@Bean(name=DataSources.SLAVE_DB,destroyMethod="close")@ConfigurationProperties(prefix = "spring.datasource-slave")public DataSource dataSourceSlave() {// 創建數據源return DataSourceBuilder.create().type(DruidDataSource.class).build();}}

Step5 Mybatis中配置成動態數據源

@Configuration 功能不多說了,如上。

@MapperScan 通過使用@MapperScan可以指定要掃描的Mapper類的包的路徑,當然了也可以在Mapper接口上聲明@Mapper , 當然是@MapperScan更方便了。

內部@Bean用到了DynamicDataSource 繼承自AbstractRoutingDataSource,就是我們剛開始說的核心

package com.artisan.config;import java.util.HashMap; import java.util.Map;import javax.sql.DataSource;import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver;@Configuration @MapperScan(basePackages = { "com.artisan.dao" }) // 掃描的mybatis接口類的包名 public class MybatisConfig {@Autowired@Qualifier(DataSources.MASTER_DB)private DataSource masterDB;@Autowired@Qualifier(DataSources.SLAVE_DB)private DataSource slaveDB;/*** 動態數據源*/@Bean(name = "dynamicDataSource")public DataSource dynamicDataSource() {DynamicDataSource dynamicDataSource = new DynamicDataSource();// 默認數據源dynamicDataSource.setDefaultTargetDataSource(masterDB);// 配置多數據源Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();dataSourceMap.put(DataSources.MASTER_DB, masterDB);dataSourceMap.put(DataSources.SLAVE_DB, slaveDB);dynamicDataSource.setTargetDataSources(dataSourceMap);return dynamicDataSource;}@Bean@ConfigurationProperties(prefix = "mybatis")public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();// 配置數據源,關鍵配置sqlSessionFactoryBean.setDataSource(dynamicDataSource());// 解決配置到配置文件中通過*配置找不到mapper文件的問題。 如果不設置這一行,在配置文件中,只能使用數組的方式一個個的羅列出來,并且要指定具體的文件名sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml"));return sqlSessionFactoryBean;}}

application.yml配置文件中新增mybatis的如下配置

# mybatis mybatis:# 映射文件的路徑 , 這個切換數據源的場景下不能配置 * 通配符,有多個 逗號隔開,繼續跟 classpath:mapper/XXX# mapper-locations: classpath:mapper/ArtisanMapper.xml # 在MybatisConfig.java#sqlSessionFactoryBean方法中通過sqlSessionFactoryBean設置classpath:mapper/*.xml ,不然每次都要改這個地方,不好維護。# 類型別名包配置,只能指定具體的包,多個配置可以使用英文逗號隔開type-aliases-package: com.artisan.domain# Mybatis SQL語句控制臺打印configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

Step6 ThreadLocal管理當前線程使用的數據源連接

package com.artisan.config;import lombok.extern.slf4j.Slf4j;/*** * 使用ThreadLocal管理當前線程使用的數據源連接* * @author yangshangwei**/ @Slf4j public class DatasourceContextHolder {public static final String DEFAULT_DATASOURCE = DataSources.MASTER_DB;private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();/*** 設置數據源* @param dbType*/public static void setDB(String dbType) {contextHolder.set(dbType);log.info("切換到數據源{}", dbType);}/*** 獲取數據源*/public static String getDB() {return contextHolder.get();}/*** 清除數據源*/public static void clearDB() {contextHolder.remove();} }

Step7 切面

通過Aspect 來處理自定義注解的橫切邏輯。

package com.artisan.aspect;import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component;import com.artisan.annotation.RouteDataSource; import com.artisan.config.DatasourceContextHolder;import java.lang.reflect.Method;/*** 通過切面對自定義切庫注解的方法進行攔截,動態的選擇數據源* * @author yangshangwei**/@Slf4j @Aspect @Component public class DynamicDataSourceAspect {/*** 前置增強,方法執行前,通過JoinPoint訪問連接點上下文的信息* * @param joinPoint*/@Before("@annotation(com.artisan.annotation.RouteDataSource)")public void beforeSwitchDataSource(JoinPoint joinPoint) {// 獲取連接點的方法簽名對象MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();// 獲取方法Method method = methodSignature.getMethod();// 設置默認的數據源為Master,防止切庫出現異常執行失敗的情況String dataSource = DatasourceContextHolder.DEFAULT_DATASOURCE;// 判斷方法上是否標注了@RouteDataSourceif (method.isAnnotationPresent(RouteDataSource.class)) {RouteDataSource routeDataSource = method.getDeclaredAnnotation(RouteDataSource.class);// 獲取@RouteDataSource上的value的值dataSource = routeDataSource.value();}// 設置數據源DatasourceContextHolder.setDB(dataSource);log.info("setDB {}", dataSource);}/*** 后置增強,清空DatasourceContextHolder,防止threadLocal誤用帶來的內存泄露*/@After("@annotation(com.artisan.annotation.RouteDataSource)")public void afterSwitchDataSource() {// 方法執行完成后,清除threadlocal中持有的databaseDatasourceContextHolder.clearDB();log.info("清空DatasourceContextHolder...");}/**@Before("@annotation(com.artisan.annotation.RouteDataSource)")public void beforeSwitchDataSource(JoinPoint point) {// 獲得當前訪問的classClass<?> className = point.getTarget().getClass();// 獲得訪問的方法名String methodName = point.getSignature().getName();// 得到方法的參數的類型Class[] argClass = ((MethodSignature) point.getSignature()).getParameterTypes();String dataSource = DatasourceContextHolder.DEFAULT_DATASOURCE;try {// 得到訪問的方法對象Method method = className.getMethod(methodName, argClass);// 判斷是否存在@DS注解if (method.isAnnotationPresent(RouteDataSource.class)) {RouteDataSource annotation = method.getAnnotation(RouteDataSource.class);// 取出注解中的數據源名dataSource = annotation.value();}} catch (Exception e) {log.error("routing datasource exception, " + methodName, e);}// 切換數據源DatasourceContextHolder.setDB(dataSource);}**/ }

Step 8 核心方法,重寫AbstractRoutingDataSource#determineCurrentLookupKey

根據上面的AOP攔截,通過DatasourceContextHolder.getDB()動態的取出在切面里設置(DatasourceContextHolder.setDB(dataSource))的數據源即可。

package com.artisan.config;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import lombok.extern.slf4j.Slf4j;@Slf4j public class DynamicDataSource extends AbstractRoutingDataSource{@Overrideprotected Object determineCurrentLookupKey() {log.info("數據源為{}", DatasourceContextHolder.getDB());return DatasourceContextHolder.getDB();} }

測試

庫表數據

Master:

-- ---------------------------- -- Table structure for artisan -- ---------------------------- DROP TABLE IF EXISTS `artisan`; CREATE TABLE `artisan` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`sex` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;-- ---------------------------- -- Records of artisan -- ---------------------------- INSERT INTO `artisan` VALUES ('1', 'master', '女'); INSERT INTO `artisan` VALUES ('2', 'master2', '男');

Slave:

-- ---------------------------- -- Table structure for artisan -- ---------------------------- DROP TABLE IF EXISTS `artisan`; CREATE TABLE `artisan` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(255) DEFAULT NULL,`sex` varchar(255) DEFAULT NULL,PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;-- ---------------------------- -- Records of artisan -- ---------------------------- INSERT INTO `artisan` VALUES ('1', 'replication1', '女'); INSERT INTO `artisan` VALUES ('2', 'replication2', '男');

Domain

package com.artisan.domain;import lombok.Data;@Data public class Artisan {private Long id ;private String name ;private String sex;}

Dao

package com.artisan.dao;import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param;import com.artisan.domain.Artisan; /*** * @author yangshangwei* * 增加@Mapper這個注解之后,Spring 啟動時會自動掃描該接口,這樣就可以在需要使用時直接注入 Mapper 了* * MybatisConfig中標注了@MapperScan , 所以這里的@Mapper不加也行*/@Mapper public interface ArtisanMapper {Artisan selectArtisanById(@Param("id") int id );}

對應的sql映射文件 ,當然了也可以使用@Select注解的方式,更簡便。

<?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接口和XML文件關聯的時候, namespace的值就需要配置成接口的全限定名稱 --> <mapper namespace="com.artisan.dao.ArtisanMapper"><select id="selectArtisanById" resultType="Artisan"> select id , name ,sex from artisan where id= #{id}</select> </mapper>

Service

接口及實現類

忽略這個方法名,忘改了。。。。事實上是根據Id獲取某個Artisan.

package com.artisan.service;import com.artisan.domain.Artisan;public interface ArtisanService {Artisan getArtisanListFromMaster(int id);Artisan getArtisanListFromSlave(int id); }

實現類

通過自定義注解設置主從庫 ,默認是主庫,@RouteDataSource(DataSources.MASTER_DB)可以省略

package com.artisan.service.impl;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service;import com.artisan.annotation.RouteDataSource; import com.artisan.config.DataSources; import com.artisan.dao.ArtisanMapper; import com.artisan.domain.Artisan; import com.artisan.service.ArtisanService;@Service public class ArtisanServiceImpl implements ArtisanService {@Autowiredprivate ArtisanMapper artisanMapper;@Override@RouteDataSource(DataSources.MASTER_DB)public Artisan getArtisanListFromMaster(int id) {return artisanMapper.selectArtisanById(id);}@Override@RouteDataSource(DataSources.SLAVE_DB)public Artisan getArtisanListFromSlave(int id) {return artisanMapper.selectArtisanById(id);}}

Controller

package com.artisan.controller;import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController;import com.artisan.domain.Artisan; import com.artisan.service.ArtisanService;@RestController public class ArtisanController {@Autowiredprivate ArtisanService artisanService ;@GetMapping("/getDataFromMaster")public Artisan getDataFromMaster(int id) {return artisanService.getArtisanListFromMaster(id);}@GetMapping("/getDataFromRep")public Artisan getDataFromRep(int id) {return artisanService.getArtisanListFromSlave(id);}}

啟動Spring Boot 工程

為了驗證功能,我們從主從庫均是查詢操作吧。

訪問主庫:
http://localhost:8080/getDataFromMaster?id=1

訪問從庫:
http://localhost:8080/getDataFromRep?id=2


為了方便用application.properties的童鞋,代碼如下,驗證通過

#master spring.datasource-master.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource-master.url=jdbc:mysql://localhost:3306/master?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false spring.datasource-master.username=root spring.datasource-master.password=root spring.datasource-master.type=com.alibaba.druid.pool.DruidDataSource#slave spring.datasource-slave.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource-slave.url=jdbc:mysql://localhost:3306/slave?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false spring.datasource-slave.username=root spring.datasource-slave.password=root spring.datasource-slave.type=com.alibaba.druid.pool.DruidDataSource#mybatis #mybatis.mapper-locations=classpath:mapper/ArtisanMapper.xml mybatis.type-aliases-package=com.artisan.domain

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 http://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>2.1.2.RELEASE</version><relativePath /> <!-- lookup parent from repository --></parent><groupId>com.artisan</groupId><artifactId>RoutingDataSource</artifactId><version>0.0.1-SNAPSHOT</version><name>RoutingDataSource</name><description>Artisan </description><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</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>1.3.2</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

代碼

https://github.com/yangshangwei/RoutingDataSource

總結

以上是生活随笔為你收集整理的Spring Boot2.x-09 基于Spring Boot 2.1.2 + Mybatis使用自定义注解实现数据库切换的全部內容,希望文章能夠幫你解決所遇到的問題。

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