目錄
一、分頁插件
1.添加配置類
2.在測試類測試
結果
二、xml實現分頁的自定義
1.UserMapper中定義接口方法
2.創建UserMapper.xml文件
?3.在測試類測試
結果
?三、樂觀鎖
1.場景
2.樂觀鎖與悲觀鎖
?3.模擬修改沖突
數據庫中添加商品表
添加數據
添加實體類
添加mapper
測試
結果
?樂觀鎖實現流程
4.Mybatis-Plus實現樂觀鎖
首先修改實體類
?添加樂觀鎖插件配置
測試?
結果
一、分頁插件
MyBatis?Plus自帶分頁插件,只要簡單的配置即可實現分頁功能。
1.添加配置類
package com.example.mybatisplus.config;import com.baomidou.mybatisplus.annotation.DbType;
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;/*** 此配置類的作用是向 Spring 容器注冊一個 MybatisPlusInterceptor 攔截器,* 該攔截器會在執行 SQL 時攔截分頁查詢語句,* 并自動處理分頁邏輯,包含計算總記錄數、總頁數等。* */
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}
2.在測試類測試
package com.example.mybatisplus;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.mybatisplus.entity.User;
import com.example.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.*;@SpringBootTest
public class Test4{@AutowiredUserMapper userMapper;@Testpublic 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());}
}
結果
二、xml實現分頁的自定義
1.UserMapper中定義接口方法
/*** 根據年齡查詢用戶列表,分頁顯示* @param page 分頁對象 ,xml中可以從里面進行取值 ,傳遞參數 Page 即自動分頁 ,必須放在第一位* @param age 年齡* @return */Page<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);
?即:
2.創建UserMapper.xml文件
在這里面能配置分頁的條件:
<?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.example.mybatisplus.mapper.UserMapper"><!--SQL片段,記錄基礎字段--><sql id="BaseColumns">id,name,age,email</sql><!--IPage<User> selectPageVo(Page<User> page, Integer age);--><select id="selectPageVo" resultType="com.example.mybatisplus.entity.User">SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > #{age}</select></mapper>
?3.在測試類測試
@Testpublic 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());}
結果
因為加了age的限制,所以這里查詢出來去分頁的數據就變少了
?三、樂觀鎖
1.場景
一件商品,成本價是80元,售價是100元。老板先是通知小李,說你去把商品價格增加50元。小?李正在玩游戲,耽擱了一個小時。正好一個小時后,老板覺得商品價格增加到150元,價格太
高,可能會影響銷量。又通知小王,你把商品價格降低30元。
此時,小李和小王同時操作商品后臺系統。小李操作的時候,系統先取出商品價格100元;小王??也在操作,取出的商品價格也是100元。小李將價格加了50元,并將100+50=150元存入了數據 ?庫;小王將商品減了30元,并將100-30=70元存入了數據庫。是的,如果沒有鎖,小李的操作就?完全被小王的覆蓋了。
那么現在商品價格是70元,比成本價低10元。幾分鐘后,這個商品很快出售了1千多件商品,老板虧1?萬多。
2.樂觀鎖與悲觀鎖
上面的故事,如果是樂觀鎖,小王保存價格前,會檢查下價格是否被人修改過了。如果被修改過了,則重新取出的被修改后的價格,150元,這樣他會將120元存入數據庫。
如果是悲觀鎖,小李取出數據后,小王只能等小李操作完之后,才能對價格進行操作,也會保證最終的價格是120元。
特性 | 悲觀鎖 | 樂觀鎖 |
---|---|---|
鎖定時機 | 讀取數據時鎖定 | 更新數據時檢查 |
沖突預防 | 通過鎖定數據來預防沖突 | 通過檢測數據變更來處理沖突 |
性能 | 通常性能較低,因為涉及到鎖的獲取和釋放 | 通常性能較高,因為沒有鎖的開銷 |
適用場景 | 高沖突環境,寫操作頻繁 | 低沖突環境,讀操作頻繁 |
實現方式 | 數據庫鎖機制,如行鎖、表鎖 | 版本號或時間戳等機制 |
用戶體驗 | 用戶可能感知到鎖的存在,如長時間等待 | 用戶通常感知不到鎖的存在 |
數據一致性 | 強一致性保證 | 最終一致性,依賴于沖突檢測和處理 |
?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.example.mybatisplus.entity;import lombok.*;@Data //lombok注解
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@ToString
public class Product {private Long id;private String name;private Integer price;private Integer version;
}
添加mapper
package com.example.mybatisplus.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mybatisplus.entity.Product;@Repository
public interface ProductMapper extends BaseMapper<Product> {}
測試
package com.example.mybatisplus;import com.example.mybatisplus.entity.Product;
import com.example.mybatisplus.mapper.ProductMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
public class Test5 {@AutowiredProductMapper productMapper;@Testpublic 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);//價格覆蓋,最后的結果:70System.out.println("最后的結果:" + p3.getPrice());}}
結果
這里為了查看方便,我先將配置文件中的日志關閉了。

可以清晰看出變化結果:?

?樂觀鎖實現流程
數據庫中添加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
4.Mybatis-Plus實現樂觀鎖
首先修改實體類

?添加樂觀鎖插件配置
在方才的分頁配置這里加一條:
//添加樂觀鎖插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

測試?
當然在測試之前,先將數據調回100:
然后再進行測試:?
/***樂觀鎖版本* */@Testpublic void testConcurrentVersionUpdate() {//小李取數據Product p1 = productMapper.selectById(1L);//小王取數據Product p2 = productMapper.selectById(1L);//小李修改 + 50p1.setPrice(p1.getPrice() + 50);int result1 = productMapper.updateById(p1);System.out.println("小李修改的結果:" + result1);//小王修改 - 30p2.setPrice(p2.getPrice() - 30);int result2 = productMapper.updateById(p2);System.out.println("小王修改的結果:" + result2);if(result2 == 0){//失敗重試,重新獲取version并更新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());}
結果
