MyBatis-Plus3

一、條件構造器和常用接口

1.wapper介紹

MyBatis-Plus 提供了一套強大的條件構造器(Wrapper),用于構建復雜的數據庫查詢條件。Wrapper 類允許開發者以鏈式調用的方式構造查詢條件,無需編寫繁瑣的 SQL 語句,從而提高開發效率并減少 SQL 注入的風險。

在 MyBatis-Plus 中,Wrapper 類是構建查詢和更新條件的核心工具。以下是主要的 Wrapper 類及其功能:

  • AbstractWrapper:這是一個抽象基類,提供了所有 Wrapper 類共有的方法和屬性。它定義了條件構造的基本邏輯,包括字段(column)、值(value)、操作符(condition)等。所有的 QueryWrapper、UpdateWrapper、LambdaQueryWrapper 和 LambdaUpdateWrapper 都繼承自 AbstractWrapper。

  • QueryWrapper:專門用于構造查詢條件,支持基本的等于、不等于、大于、小于等各種常見操作。它允許你以鏈式調用的方式添加多個查詢條件,并且可以組合使用?and?和?or?邏輯。

  • UpdateWrapper:用于構造更新條件,可以在更新數據時指定條件。與 QueryWrapper 類似,它也支持鏈式調用和邏輯組合。使用 UpdateWrapper 可以在不創建實體對象的情況下,直接設置更新字段和條件。

  • LambdaQueryWrapper:這是一個基于 Lambda 表達式的查詢條件構造器,它通過 Lambda 表達式來引用實體類的屬性,從而避免了硬編碼字段名。這種方式提高了代碼的可讀性和可維護性,尤其是在字段名可能發生變化的情況下。

  • LambdaUpdateWrapper:類似于 LambdaQueryWrapper,LambdaUpdateWrapper 是基于 Lambda 表達式的更新條件構造器。它允許你使用 Lambda 表達式來指定更新字段和條件,同樣避免了硬編碼字段名的問題。

?2.功能詳解

MyBatis-Plus 的 Wrapper 類是構建復雜查詢和更新條件的關鍵工具。它允許開發者以鏈式調用的方式構造 SQL 的 WHERE 子句,提供了極大的靈活性和便利性。

以下是對 Wrapper 功能的提示和注意事項。

提示:

條件判斷:Wrapper 方法通常接受一個?boolean?類型的參數,用于決定是否將該條件加入到最終的 SQL 中。例如:

queryWrapper.like(StringUtils.isNotBlank(name), Entity::getName, name).eq(age != null && age >= 0, Entity::getAge, age);

默認行為:如果某個方法沒有顯式提供?boolean?類型的參數,則默認為?true,即條件總是會被加入到 SQL 中。

泛型參數:Wrapper 類是泛型類,其中?Param?通常指的是 Wrapper 的子類實例,如 QueryWrapper、UpdateWrapper 等。

字段引用:在 LambdaWrapper 中,R?代表的是一個函數,用于引用實體類的屬性,例如?Entity::getId。而在普通 Wrapper 中,R?代表的是數據庫字段名。

字段名注意事項:當?R?具體類型為?String?時,表示的是數據庫字段名,而不是實體類數據字段名。如果字段名是數據庫關鍵字,需要使用轉義符包裹。

集合參數:如果方法的參數是?Map?或?List,當它們為空時,對應的 SQL 條件不會被加入到最終的 SQL 中。

學習資源:對于不熟悉的函數式編程概念,可以參考學習資源進行學習。

注意事項:

RPC 調用中的 Wrapper:不支持也不贊成在 RPC 調用中傳輸 Wrapper 對象。Wrapper 對象通常包含大量信息,不適合作為傳輸對象。正確的做法是定義一個 DTO(數據傳輸對象)進行傳輸,然后在被調用方根據 DTO 執行相應的操作。

維護性:避免在 Controller 層使用 Map 接收值,這種做法雖然開發時方便,但會給后續的維護帶來困難。

問題反饋:不接受任何關于 RPC 傳輸 Wrapper 報錯相關的 issue 或 pr。

安全性:?QueryWrapper?UpdateWrapper?字段部分,如有允許?前端傳入 SQL 片段?這可能會導致?SQL 注入風險?需要校驗,更多查看?預防安全漏洞。

3.QueryWrapper

3.1組裝查詢條件

@Test
public void test01(){
//查詢用戶名包含a,年齡在20到30之間,并且郵箱不為null的用戶信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE
is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "a")
.between("age", 20, 30)
.isNotNull("email");
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}

3.2組裝排序條件

@Test
public void test02(){
//按年齡降序查詢用戶,如果年齡相同則按id升序排列
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper
.orderByDesc("age")
.orderByAsc("id");
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

3.3組裝刪除條件

@Test
public void test03(){
//刪除email為空的用戶
//DELETE FROM t_user WHERE (email IS NULL)
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.isNull("email");
//條件構造器也可以構建刪除語句的條件
int result = userMapper.delete(queryWrapper);
System.out.println("受影響的行數:" + result);
}

3.4條件的優先級

@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//將(年齡大于20并且用戶名中包含有a)或郵箱為null的用戶信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND age > ? OR email IS NULL)
queryWrapper
.like("username", "a")
.gt("age", 20)
.or()
.isNull("email");
User user = new User();
user.setAge(18);
user.setEmail("user@qcby.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影響的行數:" + result);
}
@Test
public void test04() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//將用戶名中包含有a并且(年齡大于20或郵箱為null)的用戶信息修改
//UPDATE t_user SET age=?, email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
//lambda表達式內的邏輯優先運算
queryWrapper
.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
User user = new User();
user.setAge(18);
user.setEmail("user@qcby.com");
int result = userMapper.update(user, queryWrapper);
System.out.println("受影響的行數:" + result);
}

3.5組裝select子句

@Test
public void test05() {
//查詢用戶信息的username和age字段
//SELECT username,age FROM t_user
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.select("username", "age");
//selectMaps()返回Map集合列表,通常配合select()使用,避免User對象中沒有被查詢到的列值 為null
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper); maps.forEach(System.out::println);
}

3.6實現子查詢

@Test
public void test06() {
//查詢id小于等于3的用戶信息
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (id IN (select id from t_user where id <= 3))
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.inSql("id", "select id from t_user where id <= 3"); List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}

4.UpdateWrapper

@Test
public void test07() {
//將(年齡大于20或郵箱為null)并且用戶名中包含有a的用戶信息修改
//組裝set子句以及修改條件
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
//lambda表達式內的邏輯優先運算
updateWrapper
.set("age", 18)
.set("email", "user@qcby.com")
.like("username", "a")
.and(i -> i.gt("age", 20).or().isNull("email"));
//這里必須要創建User對象,否則無法應用自動填充。如果沒有自動填充,可以設置為null
//UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
//User user = new User();
//user.setName("張三");
//int result = userMapper.update(user, updateWrapper);
//UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
int result = userMapper.update(null, updateWrapper);
System.out.println(result);
}

5.condition

在真正開發的過程中,組裝條件是常見的功能,而這些條件數據來源于用戶輸入,是可選的,因 此我們在組裝這些條件時,必須先判斷用戶是否選擇了這些條件,若選擇則需要組裝該條件,若 沒有選擇則一定不能組裝,以免影響SQL執行的結果

5.1思路一

@Test
public void test08() {
//定義查詢條件,有可能為null(用戶未輸入或未選擇)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判斷某字符串是否不為空且長度不為0且不由空白符(whitespace) 構成
if(StringUtils.isNotBlank(username)){
queryWrapper.like("username","a");
}
if(ageBegin != null){
queryWrapper.ge("age", ageBegin);
}
if(ageEnd != null){
queryWrapper.le("age", ageEnd);
}
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

5.2思路二

上面的實現方案沒有問題,但是代碼比較復雜,我們可以使用帶condition參數的重載方法構建查 詢條件,簡化代碼的編寫

@Test
public void test08UseCondition() {
//定義查詢條件,有可能為null(用戶未輸入或未選擇)
String username = null;
Integer ageBegin = 10;
Integer ageEnd = 24;
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//StringUtils.isNotBlank()判斷某字符串是否不為空且長度不為0且不由空白符(whitespace) 構成
queryWrapper
.like(StringUtils.isNotBlank(username), "username", "a")
.ge(ageBegin != null, "age", ageBegin)
.le(ageEnd != null, "age", ageEnd);
//SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE (age >= ? AND age <= ?)
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

6.LambdaQueryWrapper

@Test
public void test09() {
//定義查詢條件,有可能為null(用戶未輸入)
String username = "a";
Integer ageBegin = 10;
Integer ageEnd = 24;
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
//避免使用字符串表示字段,防止運行時錯誤
queryWrapper
.like(StringUtils.isNotBlank(username), User::getName, username) .ge(ageBegin != null, User::getAge, ageBegin)
.le(ageEnd != null, User::getAge, ageEnd);
List<User> users = userMapper.selectList(queryWrapper);
users.forEach(System.out::println);
}

7.LambdaUpdateWrapper

@Test
public void test10() {
//組裝set子句
LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper
.set(User::getAge, 18)
.set(User::getEmail, "user@qcby.com")
.like(User::getName, "a")
.and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda 表達式內的邏輯優先運算
User user = new User();
int result = userMapper.update(user, updateWrapper);
System.out.println("受影響的行數:" + result);
}

二、插件

1.分頁插件

MyBatis Plus自帶分頁插件,只要簡單的配置即可實現分頁功能

1.1添加配置類

package com.qcby.springbootmybastplus0725.config;import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyBatisPlusConfig {/*** 配置分頁插件*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加分頁攔截器(支持MySQL、PostgreSQL等主流數據庫)interceptor.addInnerInterceptor(new PaginationInnerInterceptor());return interceptor;}
}

1.2測試

@Test
public void testPage(){
//設置分頁參數
Page<User> page = new Page<>(1, 5);
userMapper.selectPage(page, null);
//獲取分頁數據
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("當前頁:"+page.getCurrent());
System.out.println("每頁顯示的條數:"+page.getSize());
System.out.println("總記錄數:"+page.getTotal());
System.out.println("總頁數:"+page.getPages());
System.out.println("是否有上一頁:"+page.hasPrevious());
System.out.println("是否有下一頁:"+page.hasNext());
}

測試結果

2.xml自定義分頁

2.1UserMapper中定義接口方法

/**
* 根據年齡查詢用戶列表,分頁顯示
* @param page 分頁對象 ,xml中可以從里面進行取值 ,傳遞參數 Page 即自動分頁 ,必須放在第一位
* @param age 年齡
* @return */
I
Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);

2.2UserMapper.xml中編寫SQL

<!--SQL片段,記錄基礎字段-->
<sql id="BaseColumns">id,username,age,email</sql><!--IPage<User> selectPageVo(Page<User> page, Integer age);-->
<select id="selectPageVo" resultType="User">
SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > # {age}
</select>

2.3測試

@Test
public void testSelectPageVo(){
//設置分頁參數
Page<User> page = new Page<>(1, 5);
userMapper.selectPageVo(page, 20);
//獲取分頁數據
List<User> list = page.getRecords();
list.forEach(System.out::println);
System.out.println("當前頁:"+page.getCurrent());
System.out.println("每頁顯示的條數:"+page.getSize());
System.out.println("總記錄數:"+page.getTotal());
System.out.println("總頁數:"+page.getPages());
System.out.println("是否有上一頁:"+page.hasPrevious());
System.out.println("是否有下一頁:"+page.hasNext());
}

結果

3.樂觀鎖

3.1場景

一件商品,成本價是80元,售價是100元。老板先是通知小李,說你去把商品價格增加50元。小 李正在玩游戲,耽擱了一個小時。正好一個小時后,老板覺得商品價格增加到150元,價格太

高,可能會影響銷量。又通知小王,你把商品價格降低30元。

此時,小李和小王同時操作商品后臺系統。小李操作的時候,系統先取出商品價格100元;小王? 也在操作,取出的商品價格也是100元。小李將價格加50元,并將100+50=150元存入了數據? 庫;小王將商品減了30元,并將100-30=70元存入了數據庫。是的,如果沒有鎖,小李的操作就 完全被小王的覆蓋了。

現在商品價格是70元,比成本價低10元。幾分鐘后,這個商品很快出售了1千多件商品,老板虧1 萬多。

3.2樂觀鎖與悲觀鎖

上面的故事,如果是樂觀鎖,小王保存價格前,會檢查下價格是否被人修改過了。如果被修改過 了,則重新取出的被修改后的價格,? 150元,這樣他會將120元存入數據庫。

如果是悲觀鎖,小李取出數據后,小王只能等小李操作完之后,才能對價格進行操作,也會保證 最終的價格是120元。

3.3模擬修改沖突

數據庫中增加商品表

CREATE TABLE t_product
(
id BIGINT(20) NOT NULL COMMENT '主鍵ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名稱 ',
price INT(11) DEFAULT 0 COMMENT '價格 ',
VERSION INT(11) DEFAULT 0 COMMENT '樂觀鎖版本號 ',
PRIMARY KEY (id)
);

添加數據

INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人筆記本 ', 100);

添加實體

package com.qcby.mybatisplus.entity;
import lombok.Data;@Data
public class Product {
private Long id;
private String name;
private Integer price;
private Integer version;
}

添加mapper

public interface ProductMapper extends BaseMapper<Product> {
}

測試

@Test
public void testConcurrentUpdate() {//1、小李
Product p1 = productMapper.selectById(1L);
System.out.println("小李取出的價格:" + p1.getPrice());//2、小王
Product p2 = productMapper.selectById(1L);
System.out.println("小王取出的價格:" + p2.getPrice());
//3、小李將價格加了50元,存入了數據庫
p1.setPrice(p1.getPrice() + 50);
int result1 = productMapper.updateById(p1);
System.out.println("小李修改結果:" + result1);//4、小王將商品減了30元,存入了數據庫
p2.setPrice(p2.getPrice() - 30);
int result2 = productMapper.updateById(p2);
System.out.println("小王修改結果:" + result2);//最后的結果
Product p3 = productMapper.selectById(1L);
//價格覆蓋,最后的結果:70
System.out.println("最后的結果:" + p3.getPrice())

3.4樂觀鎖實現流程

數據庫中添加version字段

取出記錄時,獲取當前version

SELECT id,`name`,price,`version` FROM product WHERE id=1

更新時, version + 1,如果where語句中的version版本不對,則更新失敗

UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1

3.5Mybatis-Plus實現樂觀鎖

修改實體類

@Datacom.baomidou.mybatisplus.annotation.Version;
lombok.Data;public class Product {
private Long id;
private String name;
private Integer price;
@Version
private Integer version;
}

添加樂觀鎖插件配置

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//添加分頁插件
interceptor.addInnerInterceptor(new
PaginationInnerInterceptor(DbType.MYSQL));
//添加樂觀鎖插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor;
}

測試修改沖突

小李查詢商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小王查詢商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小李修改商品價格,自動將version+1
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人筆記本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
小王修改商品價格,此時version已更新,條件不成立,修改失敗
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人筆記本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
最終,小王修改失敗,查詢價格:  150
SELECT id,name,price,version FROM t_product WHERE id=?

優化流程

//樂觀鎖測試@Testpublic void test02(){//小李取數據Product p1 = productMapper.selectById(1L);//小王取數據Product p2 = productMapper.selectById(1L);//小李修改p1.setPrice(p1.getPrice() + 50);int result1 = productMapper.updateById(p1);System.out.println("小李修改結果:" + result1);//小王修改p2.setPrice(p2.getPrice() - 30);int result2 = productMapper.updateById(p2);System.out.println("小王修改結果:" + result2);if(result2 ==0){p2 = productMapper.selectById(1L);p2.setPrice(p2.getPrice() - 30);result2 = productMapper.updateById(p2);}System.out.println("小王修改結果:" + result2);//老板查詢商品價格Product p3 = productMapper.selectById(1L);System.out.println("老板查詢價格:" + p3.getPrice());}

結果

三、通用枚舉

表中的有些字段值是固定的,例如性別(男或女),此時我們可以使用MyBatis-Plus的通用枚舉 來實現

1.數據庫表添加字段sex

2.創建通用枚舉類型

package com.qcby.springbootmybastplus0725.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;@Getter
public enum SexEnum {MALE(1,"男"),FEMALE(2,"女");@EnumValueprivate Integer sex;private String sexname;SexEnum(Integer sex, String sexname){this.sex = sex;this.sexname = sexname;}
}

3.配置掃描通用枚舉

mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:# 配置MyBatis-Plus操作表的默認前綴table-prefix: t_# 配置MyBatis-Plus的主鍵策略id-type: auto#配置掃面通用枚舉type-enums-package: com.qcby.springbootmybastplus0725.enums

4.測試

package com.qcby.springbootmybastplus0725;import com.qcby.springbootmybastplus0725.entity.User;
import com.qcby.springbootmybastplus0725.enums.SexEnum;
import com.qcby.springbootmybastplus0725.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class EnumsTest {@Autowiredprivate UserMapper userMapper;@Testpublic void test01(){User user = new User();user.setName("Enum");user.setAge(20);user.setSex(SexEnum.MALE);userMapper.insert(user);}
}

四、代碼生成器

1.引入依賴

<!--代碼生成器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.31</version></dependency>

?2.快速生成

package com.qcby.springbootmybastplus0725.util;import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.util.Collections;public class FastAutoGeneratorTest {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true", "root", "lrh123456").globalConfig(builder -> {builder.author("qcby") // 設置作者.enableSwagger() // 開啟 swagger 模式.fileOverride()// 覆蓋已生成文件.outputDir("D:\\code\\springboot\\springbootmybastplus0725\\src\\main\\java");}).packageConfig(builder -> {builder.parent("com.qcby.springbootmybastplus0725") // 設置父包名.moduleName("mybatisplus") // 設置父包模塊名.pathInfo(Collections.singletonMap(OutputFile.mapperXml,"D://mybatis_plus"));}).strategyConfig(builder -> {builder.addInclude("t_user") // 設置需要生成的表名.addTablePrefix("t_", "c_");}).templateEngine(new FreemarkerTemplateEngine()).execute();}
}

結果

五、多數據源

適用于多種場景:純粹多庫、?? 讀寫分離、??? 一主多從、?? 混合模式等

目前我們就來模擬一個純粹多庫的一個場景,其他場景類似

場景說明

創建兩個庫,

分別為:? mybatis_plus(以前的庫不動)與mybatis_plus_1? (新建),

mybatis_plus庫的product表移動到mybatis_plus_1庫,這樣每個庫一張表,通過一個測試用例 分別獲取用戶數據與商品數據,如果獲取到說明多庫模擬成功

1.創建數據庫及表

創建數據庫mybatis_plus_1和表product

CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */; use `mybatis_plus_1`;
CREATE TABLE product
(
id BIGINT(20) NOT NULL COMMENT '主鍵ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名稱 ',
price INT(11) DEFAULT 0 COMMENT '價格 ',
version INT(11) DEFAULT 0 COMMENT '樂觀鎖版本號 ',
PRIMARY KEY (id)
);

添加測試數據

INSERT INTO product (id, NAME, price) VALUES (1, '外星人筆記本 ', 100);

刪除mybatis_plus庫中的product

2.引入依賴

<!--多數據源--><dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.0</version></dependency>

3.配置多數據源

說明:注釋掉之前的數據庫連接,添加新配置

spring:datasource:dynamic:# 設置默認的數據源或者數據源組 ,默認值即為masterprimary: master# 嚴格匹配數據源 ,默認false.true未匹配到指定數據源時拋異常 ,false使用默認數據源strict: falsedatasource:master:url: jdbc:mysql://localhost:3306/mybatis_plus?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: rootslave_1:url: jdbc:mysql://localhost:3306/mybatis_plus_1?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: root

4.創建用戶service

package com.qcby.springbootmybastplus0725.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qcby.springbootmybastplus0725.entity.User;public interface UserService extends IService<User> {}
package com.qcby.springbootmybastplus0725.service.impl;import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcby.springbootmybastplus0725.entity.User;
import com.qcby.springbootmybastplus0725.mapper.UserMapper;
import com.qcby.springbootmybastplus0725.service.UserService;
import org.springframework.stereotype.Service;@DS("master")//指定所操作的數據源
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

6.創建商品service

package com.qcby.springbootmybastplus0725.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.qcby.springbootmybastplus0725.entity.Product;public interface ProductService extends IService<Product> {
}
package com.qcby.springbootmybastplus0725.service.impl;import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qcby.springbootmybastplus0725.entity.Product;
import com.qcby.springbootmybastplus0725.mapper.ProductMapper;
import com.qcby.springbootmybastplus0725.service.ProductService;
import org.springframework.stereotype.Service;@DS("slave_1")
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {}

6.測試

package com.qcby.springbootmybastplus0725;import com.qcby.springbootmybastplus0725.service.ProductService;
import com.qcby.springbootmybastplus0725.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class DynamicTest {@Autowiredprivate ProductService productService;@Autowiredprivate UserService userService;@Testpublic void test(){System.out.println(userService.list());System.out.println(productService.list());}
}

結果

1、都能順利獲取對象,則測試成功

2、如果我們實現讀寫分離,將寫操作方法加上主庫數據源,讀操作方法加上從庫數據源,自動切 換,是不是就能實現讀寫分離?

六、MyBatisX插件

MyBatis-Plus為我們提供了強大的mapperservice模板,能夠大大的提高開發效率

但是在真正開發過程中,? MyBatis-Plus并不能為我們解決所有問題,例如一些復雜的SQL,多表 聯查,我們就需要自己去編寫代碼和SQL語句,我們該如何快速的解決這個問題呢,這個時候可 以使用MyBatisX插件

MyBatisX一款基于 IDEA 的快速開發插件,為效率而生

MyBatisX插件用法:https://baomidou.com/plugins/

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93933.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93933.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93933.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

GXP6040K壓力傳感器可應用于醫療/汽車/家電

GXP6040K 系列壓力傳感器是一種超小型&#xff0c;為設備小型化做出貢獻的高精度半導體壓力傳感器&#xff0c;適用于生物醫學、汽車電子、白色家電等領域。采用標準的SOP6 和 DIP6 封裝形式&#xff0c;方便用戶進行多種安裝方式。 內部核心芯片是利用 MEMS&#xff08;微機械…

Android ConstraintLayout 使用詳解

什么是 ConstraintLayoutConstraintLayout&#xff08;約束布局&#xff09;是 Android Studio 2.2 引入的一種新型布局&#xff0c;現已成為 Android 開發中最強大、最靈活的布局管理器之一。它結合了 RelativeLayout 的相對定位和 LinearLayout 的線性布局優勢&#xff0c;能…

Unity3D數學第三篇:坐標系與變換矩陣(空間轉換篇)

Unity3D數學第一篇&#xff1a;向量與點、線、面&#xff08;基礎篇&#xff09; Unity3D數學第二篇&#xff1a;旋轉與歐拉角、四元數&#xff08;核心變換篇&#xff09; Unity3D數學第三篇&#xff1a;坐標系與變換矩陣&#xff08;空間轉換篇&#xff09; Unity3D數學第…

UV安裝并設置國內源

文章目錄一、UV下載1.官方一鍵安裝2.github下載安裝二、更換國內鏡像源&#xff08;加速下載&#xff09;方法1&#xff1a;臨時環境變量&#xff08;單次生效&#xff09;方法2&#xff1a;永久配置&#xff08;推薦&#xff09;方法3&#xff1a;命令行直接指定源三、驗證鏡像…

1 前言:什么是 CICD 為什么要學 CICD

什么是 CI/CD 我的資源庫網站&#xff1a;https://www.byteooo.cn 在開發階段&#xff0c;許多編譯工具會將我們的源碼編譯可使用的文件。例如 vue-cli 的項目會被 webpack 打包編譯為瀏覽器的文件&#xff0c;Java 項目會被編譯為 .class/jar 文件以供服務器使用。 但是&am…

GitHub 趨勢日報 (2025年07月30日)

&#x1f4ca; 由 TrendForge 系統生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日報中的項目描述已自動翻譯為中文 &#x1f4c8; 今日獲星趨勢圖 今日獲星趨勢圖3579copyparty752supervision664500-AI-Agents-Projects483awesome403prompt-optim…

“非參數化”大語言模型與RAG的關系?

這個問題觸及了一個關鍵的技術細節&#xff0c;兩者關系密切&#xff0c;但層面不同&#xff1a; “非參數化”大語言模型是一個更廣泛的概念或類別&#xff0c;而RAG&#xff08;Retrieval-Augmented Generation&#xff09;是實現這一概念最主流、最具體的一種技術框架。 您可…

LeetCode Hot 100:15. 三數之和

題目給你一個整數數組 nums &#xff0c;判斷是否存在三元組 [nums[i], nums[j], nums[k]] 滿足 i ! j、i ! k 且 j ! k &#xff0c;同時還滿足 nums[i] nums[j] nums[k] 0 。請你返回所有和為 0 且不重復的三元組。注意&#xff1a;答案中不可以包含重復的三元組。示例 1&…

銀行回單識別應用場景剖析

銀行回單OCR識別技術通過自動化處理紙質或電子回單中的關鍵信息&#xff0c;顯著提升了金融、企業及個人場景下的數據管理效率。以下是其核心應用場景及價值的詳細剖析&#xff1a;一、企業財務場景自動化賬務處理對賬與記賬&#xff1a;OCR自動提取交易日期、金額、賬號等信息…

React的介紹和特點

1. React是什么&#xff1f; 1.1. React&#xff1a; 用于構建用戶界面的JavaScript庫1.2. React的官網文檔&#xff1a;https://zh-hans.reactjs.org/ 2. React的特點2.1. 聲明式編程&#xff1a; 目前整個大前端開發的模式&#xff1a;Vue、React、Flutter、SwiftUI只需要維護…

內核smmu學習

思考 smmu對外提供功能&#xff0c;設備驅動調用smmu 提供的api來配置頁表&#xff0c;那其他設備是如何和smmu交互的&#xff1f;iommu 作為將不同smmu硬件的一個抽象封裝&#xff0c;其它設備應該只能看到iommu這個封裝層&#xff0c;那么iommu這個子系統是如何進行抽象的&a…

Android Slices:讓應用功能在系統級交互中觸手可及

引言 在當今移動應用生態中&#xff0c;用戶每天要面對數十個甚至上百個應用的選擇&#xff0c;如何讓自己的應用在關鍵時刻觸達用戶&#xff0c;成為開發者面臨的重要挑戰。Google在Android 9 Pie中引入的Slices技術&#xff0c;正是為了解決這一痛點而生。本文將全面介紹And…

python學智能算法(三十))|SVM-KKT條件的數學理解

【1】引言 前序學習進程中&#xff0c;通過類比力的平衡對KKT條件進行了初步的理解。 今天我們更進一步&#xff0c;常使用數學語言進一步解釋KKT條件。 【2】帶約束的最小優化問題 首先定義一個即將求解的優化問題&#xff1a; 目標函數&#xff1a;最小化f(x)(x∈Rn)f(x)(…

華為云Flexus+DeepSeek征文|Linux命令實現兩種部署的性能捕獲+(硅基+Maas)模型添加教學

前引&#xff1a;“在數字化浪潮洶涌澎湃的今天&#xff0c;企業對云計算服務的需求已從基礎架構支撐&#xff0c;逐步轉向更深層次的AI賦能與業務創新驅動。面對復雜多變的市場環境&#xff0c;選擇一個強大、可靠且具備前瞻性的云服務伙伴&#xff0c;無疑是企業實現高速增長…

langchain--1--prompt、output格式、LCEL示例

環境&#xff1a;本地使用ollama部署的deepseek-r1:1.5b模型 本文示例包含: [1] 非LCEL的調用方法[2] LCEL的調用方法[3] prompt template的簡單使用&#xff0c;除了PromptTemplate模板&#xff0c;還有一些其它模板&#xff0c;可去查看官網[4] 輸出&#xff1a;json格式、py…

【算法】指數滑動濾波器

指數滑動濾波器作用原理特點公式代碼優化升級作用 首先這個濾波器能夠將一些突變的信號對系統的影響降低&#xff0c;能夠平滑輸入信號&#xff0c;濾除噪聲&#xff0c;減少測量數據的瞬間波動和干擾&#xff0c;就是實現輸入信號不能不變&#xff0c;數值不會突然變大&#…

STM32F4—電源管理器

Power supply schemesPower supply supervisorInternal reset ON有PDR_ON pin的MCU&#xff0c;PDR_ON pin被拉高的時候電源監視器被使能。沒有PDR_ON pin的MCU默認一直使能。內部集成了power-on reset (POR) / power-down reset (PDR)POR&#xff08;上電復位&#xff09;&…

MySQL鎖的分類 MVCC和S/X鎖的互補關系

各位看官&#xff0c;大家早安午安晚安呀~~~如果您覺得這篇文章對您有幫助的話歡迎您一鍵三連&#xff0c;小編盡全力做到更好 歡迎您分享給更多人哦今天我們來學習&#xff1a;MySQL鎖的分類 && MVCC和S/X鎖的互補關系1.鎖分類1.按鎖粒度分類&#xff1a;全局鎖&#…

第五屆智能通信與計算國際學術會議(ICICC 2025)

重要信息 官網&#xff1a;www.ic-icc.org 時間&#xff1a;2025年8月15-16日 地點&#xff1a;中國 南京 第五屆智能通信與計算國際學術會議(ICICC 2025&#xff09;定于2025年8月15-16日在中國 南京舉行。隨著信息技術的飛速發展&#xff0c;智能通信與計算領域的研究與…

基于C#和NModbus4庫實現的Modbus RTU串口通信

基于C#和NModbus4庫實現的Modbus RTU串口通信&#xff0c;包含完整的界面設計和功能實現&#xff1a;一、項目依賴配置NuGet包安裝&#xff1a; Install-Package NModbus4 Install-Package System.IO.Ports窗體控件布局&#xff1a; <!-- 基礎控件配置 --> <ComboBox …