重慶做網站個人谷歌搜索引擎下載
MyBatis-Plus
1、mybatis-plus介紹
官網:https://baomidou.com/
MyBatis-Plus (簡稱 MP)是一個MyBatis的增強工具,在 MyBatis 的基礎上只做增強不做改變,為簡化開發(fā)、提高效率而生。MyBatis-Plus提供了通用的mapper和service,可以在不編寫任何SQL語句的情況下,快速的實現對單表的CRUD批量、邏輯刪除、分頁等操作。
1.1、特性
- 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
- 損耗小:啟動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作
- 強大的 CRUD 操作:內置通用 Mapper、通用 Service,僅僅通過少量配置即可實現單表大部分 CRUD 操作,更有強大的條件構造器,滿足各類使用需求
- 支持 Lambda 形式調用:通過 Lambda 表達式,方便的編寫各類查詢條件,無需再擔心字段寫錯
- 支持主鍵自動生成:支持多達 4 種主鍵策略(內含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式調用,實體類只需繼承 Model 類即可進行強大的 CRUD 操作
- 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 內置代碼生成器:采用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用
- 內置分頁插件:基于 MyBatis 物理分頁,開發(fā)者無需關心具體操作,配置好插件之后,寫分頁等同于普通 List 查詢
- 分頁插件支持多種數據庫:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數據庫
- 內置性能分析插件:可輸出 SQL 語句以及其執(zhí)行時間,建議開發(fā)測試時啟用該功能,能快速揪出慢查詢
- 內置全局攔截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規(guī)則,預防誤操作
1.2、支持數據庫
任何能使用 MyBatis
進行 CRUD, 并且支持標準 SQL 的數據庫,具體支持情況如下,如果不在下列表查看分頁部分教程 PR 您的支持。
- MySQL,Oracle,DB2,H2,HSQL,SQLite,PostgreSQL,SQLServer,Phoenix,Gauss ,ClickHouse,Sybase,OceanBase,Firebird,Cubrid,Goldilocks,csiidb,informix,TDengine,redshift
- 達夢數據庫,虛谷數據庫,人大金倉數據庫,南大通用(華庫)數據庫,南大通用數據庫,神通數據庫,瀚高數據庫,優(yōu)炫數據庫
1.3、代碼托管
- gitee:https://gitee.com/baomidou/mybatis-plus
- GitHub:https://github.com/baomidou/mybatis-plus
2、入門案例
2.1、開發(fā)環(huán)境
IDE: IDEA 2023.1
JDK:JDK8+
構建工具:maven3.8.3
MySQL版本:MySQL5.5.27
SpringBoot:2.7.6
mybatis-plus:3.5.3.1
2.2、創(chuàng)建數據庫及表
-- 創(chuàng)建數據庫
CREATE DATABASE mybatisplus;
-- 使用數據庫
USE mybatisplus;
-- 創(chuàng)建表
CREATE TABLE USER
(id BIGINT(20) NOT NULL COMMENT '主鍵ID',NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',age INT(11) NULL DEFAULT NULL COMMENT '年齡',email VARCHAR(50) NULL DEFAULT NULL COMMENT '郵箱',PRIMARY KEY (id)
);
2.3、表中添加數據
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
2.4、創(chuàng)建SpringBoot項目
使用Spring Initializr
快速構建SpringBoot項目
項目中沒有用途的幾個文件可以刪除
2.5、添加依賴
<?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>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.qbzaixian</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>demo</description><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><!-- mybatis-plus的啟動器--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- lombok簡化實體類開發(fā),需要idea安裝lombok的插件哦~~~--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.34</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2.6、配置文件
spring:datasource:# 配置數據源類型type: com.zaxxer.hikari.HikariDataSource# 配置連接數據庫的驅動driver-class-name: com.mysql.jdbc.Driver# 配置連接數據庫的urlurl: jdbc:mysql://127.0.0.1:3306/mybatisplus?characterEncoding=utf-8&useSSL=false# 配置連接數據庫的賬號username: root# 配置連接數據庫的密碼password: 123
2.7、配置實體類
@Data
public class User {private Long id;private String name;private Integer age;private String email;
}
2.8、編寫mapper接口
mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我們的接口只需要去繼承這個接口,基本就可以實現對單表的CRUD操作。接口的泛型對應編寫的實體類
public interface UserMapper extends BaseMapper<User> {
}
2.9、編寫啟動類
在 Spring Boot 啟動類中添加
@MapperScan
注解,掃描 Mapper 文件夾:
@SpringBootApplication
@MapperScan("com.qbzaixian.mapper")
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}
}
2.10、編寫測試類
UserMapper 中的
selectList()
方法的參數為 MP 內置的條件封裝器Wrapper
,所以不填寫就是無任何條件
@SpringBootTest
public class UserTest {@Autowiredprivate UserMapper userMapper;@Testpublic void testSelect(){// 接口中提供的selectList參數為條件,如果沒有設置為nullList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);}
}
這里我選擇降低SpringBoot的版本為2.7.6來解決這個問題。
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.6</version><relativePath/> <!-- lookup parent from repository --></parent
運行測試類,看到如下測試結果
通過以上幾個簡單的步驟,我們就實現了 User 表的 CRUD 功能,甚至連 XML 文件都不用編寫!
從以上步驟中,我們可以看到集成
MyBatis-Plus
非常的簡單,只需要引入 starter 工程,并配置 mapper 掃描路徑即可。
2.11、添加日志
希望看到mybatis-plus執(zhí)行的具體過程和對應的sql語句,需要簡單的配置mybatis-plus的日志即可
# 配置日志
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
3、BaseMapper接口
mybatis-plus提供了BaseMapper接口,其中提供大量的CRUD的方法,我們的接口只需要去繼承這個接口,基本就可以實現對單表的CRUD操作。接口的泛型對應編寫的實體類
3.1、insert插入
參數說明
類型 參數名 描述 T entity 實體對象 // 插入一條記錄 int insert(T entity);
// BaseMapper的新增功能// INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )@Testpublic void testInset(){User user = new User();user.setAge(20);user.setName("趙四");user.setEmail("zhaosi@qq.com");int result = this.userMapper.insert(user);System.out.println(result);}
溫馨提示:添加完成之后,添加的新對象也會進行ID回顯的,但ID值默認采用的是雪花算法計算出來的數據,不是一個自增長的值。
3.2、delete刪除
參數說明
類型 參數名 描述 Wrapper wrapper 實體對象封裝操作類(可以為 null) Collection<? extends Serializable> idList 主鍵 ID 列表(不能為 null 以及 empty) Serializable id 主鍵 ID Map<String, Object> columnMap 表字段 map 對象 // 根據 entity 條件,刪除記錄 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 刪除(根據ID 批量刪除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根據 ID 刪除 int deleteById(Serializable id); // 根據 columnMap 條件,刪除記錄 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// BaseMapper的刪除功能@Testpublic void testDel(){// 根據id刪除 DELETE FROM user WHERE id=?int i = this.userMapper.deleteById(1L);// 根據實體對象的id刪除 DELETE FROM user WHERE id=?User user= new User();user.setId(2L);int i2 = this.userMapper.deleteById(user);// 根據Collection集合刪除(id列表)// DELETE FROM user WHERE id IN ( ? , ? , ? )List<Long> list = Arrays.asList(2L, 3L, 4L);this.userMapper.deleteBatchIds(list);// 根據指定的列屬性刪除// DELETE FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("age",20);map.put("name","趙四");int i3 = this.userMapper.deleteByMap(map);}
3.3、update修改
在調用
updateById
方法前,需要在T entity
(對應的實體類)中的主鍵屬性上加上@TableId
注解。參數說明
類型 參數名 描述 T entity 實體對象 (set 條件值,可為 null) Wrapper updateWrapper 實體對象封裝操作類(可以為 null,里面的 entity 用于生成 where 語句) // 根據 whereWrapper 條件,更新記錄 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper); // 根據 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
@Testpublic void testUpdate(){// 根據id修改數據 UPDATE user SET name=?, email=? WHERE id=?User user = new User();user.setId(5L);user.setName("喬治");user.setEmail("qiaozhi@.qq.com");int i = this.userMapper.updateById(user);}
3.4、select查詢
參數說明
類型 參數名 描述 Serializable id 主鍵 ID Wrapper queryWrapper 實體對象封裝操作類(可以為 null) Collection<? extends Serializable> idList 主鍵 ID 列表(不能為 null 以及 empty) Map<String, Object> columnMap 表字段 map 對象 IPage page 分頁查詢條件(可以為 RowBounds.DEFAULT) // 根據 ID 查詢 T selectById(Serializable id); // 根據 entity 條件,查詢一條記錄 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查詢(根據ID 批量查詢) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根據 entity 條件,查詢全部記錄 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查詢(根據 columnMap 條件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根據 Wrapper 條件,查詢全部記錄 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢全部記錄。注意: 只返回第一個字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根據 entity 條件,查詢全部記錄(并翻頁) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢全部記錄(并翻頁) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根據 Wrapper 條件,查詢總記錄數 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
-- 由于前面演示刪除,修改等操作,數據庫中的數據已經不多了,現在給數據庫中重新插入部分數據
INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com');
@Testpublic void testSelectAll(){// selectList:根據條件查詢,如果沒有條件書寫為null// SELECT id,name,age,email FROM userList<User> users = this.userMapper.selectList(null);users.forEach(System.out::println);// selectById:根據id查詢// SELECT id,name,age,email FROM user WHERE id=?User user = this.userMapper.selectById(5L);System.out.println(user);// selectCount:查詢滿足條件的記錄數,如果沒有條件書寫為null// SELECT COUNT( * ) AS total FROM userLong count = this.userMapper.selectCount(null);System.out.println(count);// selectBatchIds:通過多個id列表查詢// SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )List<User> userList = this.userMapper.selectBatchIds(Arrays.asList(1L, 2L, 3L));userList.forEach(System.out::println);// selectByMap:根據指定map中的屬性作為查詢條件// SELECT id,name,age,email FROM user WHERE name = ? AND age = ?Map<String,Object> map = new HashMap<>();map.put("name","喬治");map.put("age","20");List<User> list = this.userMapper.selectByMap(map);list.forEach(System.out::println);}
3.5、自定義接口方法
mybatis-plus主要是單表的操作,如果我們需要執(zhí)行多表,或者執(zhí)行自己書寫的sql腳本與接口,mybatis-plus也支持指定的方式。
在mybaits-plus的
MybatisPlusProperties
類中配置相關的屬性配置,其中private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};
用來加載指定路徑下的xml映射文件的,當然如果你不喜歡這個路徑,可以在SpringBoot的配置文件中進行修改
mybatis-plus: config-location: 書寫自己的路徑
在項目
resoureces
目錄下,創(chuàng)建mapper
文件夾,在其中創(chuàng)建UserMapper.xml
文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.qbzaixian.mapper.UserMapper"><select id="selectUserById" resultType="map">select * from user where id = #{id}</select> </mapper>
在接口中編寫對應的查詢方法
public interface UserMapper extends BaseMapper<User> {/*** 根據指定的id,查詢user數據,返回map集合* @param id* @return*/public Map<String,Object> selectUserById(Long id); }
執(zhí)行查詢操作:
@Testpublic void testMyQuery(){// 測試自定義的接口方法Map<String, Object> map = this.userMapper.selectUserById(1L);System.out.println(map);}
4、通用Service接口
mybatis-plus不僅提供通用的mapper接口,還提供通用的Service接口。
- 通用 Service CRUD 封裝IService (opens new window)接口,進一步封裝 CRUD 采用
get 查詢單行
remove 刪除
list 查詢集合
page 分頁
前綴命名方式區(qū)分Mapper
層避免混淆, - 泛型
T
為任意實體對象 - 建議如果存在自定義通用 Service 方法的可能,請創(chuàng)建自己的
IBaseService
繼承Mybatis-Plus
提供的基類 - 對象
Wrapper
為 條件構造器
4.1、IService接口
Mybatis-Plus中提供的IService接口和實現類ServiceImpl,封裝了常見業(yè)務邏輯,可以簡單查閱源碼
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {// 提供了大量的CRUD相關的方法
}
4.2、測試Service接口
通過前面的mapper接口的CRUD演示,針對Service接口中的大部分方法使用基本一致,這里就簡單演示批量插入操作
雖然有ServiceImpl實現類和IService接口,但是大部分情況下還是需要根據對應的業(yè)務書寫相關接口和實現類
// 在service包下創(chuàng)建UserService接口
public interface UserService extends IService<User> {
}
// 在service.impl包下創(chuàng)建UserServiceImpl實現類
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
準備進行測試
@SpringBootTest
public class UserServiceTest {@Autowiredprivate UserServiceImpl userService;@Testpublic void testSaveBatch(){List<User> list = new ArrayList<>();for (int i = 1 ; i < 5 ; i++){User user = new User();user.setName("測試"+i);user.setAge(20+i);user.setEmail("test@163.com");list.add(user);}// 批量給數據庫中插入數據,在mapper接口中是沒有的boolean b = this.userService.saveBatch(list);System.out.println(b);}
}
5、注解介紹
mybatis-plus提供部分的注解,方便快速的進行表、屬性、主鍵、主鍵生成策略等進行標注
5.1、@TableName注解
- 描述:表名注解,標識實體類對應的表
- 使用位置:實體類
@Data
@TableName("user")
public class User {private Long id;private String name;private Integer age;private String email;
}
一般用在實體類名與對應的表名不一致的情況下,當然如果項目中整個表名都有對應的前綴,也可以在SpringBoot的核心配置文件進行前綴的配置,省去注解的配置
mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 全局配置表名的前綴 global-config:db-config:table-prefix: tb_
5.2、@TableId注解
用于標注當前的實體類的id屬性對應表的主鍵,mybatis-plus默認采用id屬性作為主鍵。
- 描述:主鍵注解
- 使用位置:實體類主鍵字段
@Data
@TableName("user")
public class User {@TableIdprivate Long id;private String name;private Integer age;private String email;
}
TableId注解的兩個屬性作用:
屬性 | 類型 | 必須指定 | 默認值 | 描述 |
---|---|---|---|---|
value | String | 否 | “” | 主鍵字段名 |
type | Enum | 否 | IdType.NONE | 指定主鍵類型 |
mybatis-plus在設置逐漸的時候ltype屬性用來設置主鍵的生成策略:
@Data @TableName("user") public class User {@TableId(value='對應的表主鍵列名' , type=主鍵策略)private Long id;private String name;private Integer age;private String email; }
關于type屬性(主鍵策略)的枚舉值IdType的詳細介紹:
常用的有:
@TableId(value='對應的表主鍵列名' , type=idType.AUTO) @TableId(value='對應的表主鍵列名' , type=idType.ASSIGN_ID)
值 | 描述 |
---|---|
AUTO | 數據庫 ID 自增 |
NONE | 無狀態(tài),該類型為未設置主鍵類型(注解里等于跟隨全局,全局里約等于 INPUT) |
INPUT | insert 前自行 set 主鍵值 |
ASSIGN_ID | 分配 ID(主鍵類型為 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默認實現類為DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配 UUID,主鍵類型為 String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默認 default 方法) |
ID_WORKER | 分布式全局唯一 ID 長整型類型(please use ASSIGN_ID ) |
UUID | 32 位 UUID 字符串(please use ASSIGN_UUID ) |
ID_WORKER_STR | 分布式全局唯一 ID 字符串類型(please use ASSIGN_ID ) |
關于主鍵生成策略,可以在SpringBoot的全局配置文件中進行配置
# 配置日志 mybatis-plus: configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 全局配置 global-config:db-config:# 配置表的名的前綴table-prefix: tb_# 配置主鍵生成策略id-type: auto
5.3、雪花算法
SnowFlake 中文意思為雪花,故稱為雪花算法。最早是 Twitter 公司在其內部用于分布式環(huán)境下生成唯一 ID。在2014年開源 scala 語言版本。
雪花算法的原理就是生成一個的 64 位比特位的 long 類型的唯一 id。
- 最高 1 位固定值 0,因為生成的 id 是正整數,如果是 1 就是負數了。
- 接下來 41 位存儲毫秒級時間戳,2^41/(1000606024365)=69,大概可以使用 69 年。
- 再接下 10 位存儲機器碼,包括 5 位 datacenterId 和 5 位 workerId。最多可以部署 2^10=1024 臺機器。
- 最后 12 位存儲序列號。同一毫秒時間戳時,通過這個遞增的序列號來區(qū)分。即對于同一臺機器而言,同一毫秒時間戳下,可以生成 2^12=4096 個不重復 id。
- 可以將雪花算法作為一個單獨的服務進行部署,然后需要全局唯一 id 的系統,請求雪花算法服務獲取 id 即可。
對于每一個雪花算法服務,需要先指定 10 位的機器碼,這個根據自身業(yè)務進行設定即可。例如機房號+機器號,機器號+服務號,或者是其他可區(qū)別標識的 10 位比特位的整數值都行。
簡單說雪花算法解決的問題:
- 需要選擇合適的方案應對數據規(guī)模化的增長,以應對逐漸增長的訪問壓力與數據量。
- 數據庫擴展方式主要包括:業(yè)務分庫、主從復制、數據庫分表等。
不同的編程語言,都有雪花算法的實現,可以直接調用對應的程序即可得到一個雪花算法值。
5.4、@TableField注解
作用:字段注解(非主鍵),解決表的屬性名與實體中的屬性名不一致問題。
@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;
}
@TableField注解的屬性比較多,常用的就是標注列名(即value屬性,默認可以不寫)
其他的屬性如果需要,可以參考官方文檔
5.5、@TableLogic注解
作用:表字段邏輯處理注解(邏輯刪除)
物理刪除:真實刪除,將對應的數據從數據庫中刪除,之后查詢不到此條被刪除的數據
邏輯刪除:假刪除,將對應的數據中代表是否刪除的字段狀態(tài)修改
被刪除
,在數據庫可以查到這條數據記錄,只是標記為被刪除
使用場景:可以進行數據恢復
@Data
@TableName("user")
public class User {@TableIdprivate Long id;@TableField("nickname")private String name;private Integer age;private String email;@TableLogicprivate Integer isDeleted;
}
注意:
- 上面使用
isDeleted
屬性來標準邏輯刪除,就需要在表中添加這么一列,可以將未刪除的狀態(tài)設置為0,刪除就對應的為1, - 在進行查詢時候,默認會添加
where is_deleted = 0
表示只查詢未被刪除的, - 在邏輯刪除的時候,默認會將
is_deleted
的值設置為1
6、條件構造器
6.1、條件構造器介紹
在使用mybatis-plus的時候,發(fā)現修改、刪除、查詢的都對應有Wrapper,他就是用于構建各種條件。是構造條件的頂級父類。
- Wrapper:條件構造器
- AbstractWrapper:用于條件封裝,生成sql的where條件
- QueryWrapper:查詢條件封裝
- UpdateWrapper:修改條件封裝
- AbstractLambdaWrapper:使用Lambda語法
- LambdaQueryWrapper:用于Lambda語法使用的查詢Wrapper
- LambdaUpdateWrapper:Lambda更新封裝Wrapper
- AbstractWrapper:用于條件封裝,生成sql的where條件
6.2、QueryWrapper查詢條件
@Testpublic void testQueryWrapper(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查詢姓名中包含字母o,郵箱不為null,年齡大于等于20的wrapper.like("name","o").isNotNull("email").ge("age",20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)
6.3、QueryWrapper排序條件
@Testpublic void testQueryWrapper2(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查詢年齡在20到30之間的,先按照年齡降序,年齡相同在按照姓名升序wrapper.between("age",20,30).orderByDesc("age").orderByAsc("name");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ?) ORDER BY age DESC,name ASC
6.4、QueryWrapper刪除條件
@Testpublic void testQueryWrapper3(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 刪除郵箱為空的數據wrapper.isNull("email");int i = this.userMapper.delete(wrapper);System.out.println(i);}
最終生成的sql:DELETE FROM user WHERE (email IS NULL)
6.5、QueryWrapper實現修改
@Testpublic void testQueryWrapper4(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 修改name中包含字母a的數據wrapper.like("name","a");User user = new User();user.setAge(26);user.setEmail("bbb@qq.com");int i = this.userMapper.update(user, wrapper);System.out.println(i);}
最終生成的sql:UPDATE user SET age=?, email=? WHERE (name LIKE ?)
6.6、QueryWrapper查詢指定的列
@Testpublic void testQueryWrapper5(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查詢name、age兩列數據wrapper.select("name","age");List<Map<String, Object>> maps = this.userMapper.selectMaps(wrapper);maps.forEach(System.out::println);}
最終生成的sql:SELECT name,age FROM user
6.7、QueryWrapper更改查詢的條件優(yōu)先級
默認情況下,使用QueryWrapper進行條件組裝的時候,多個條件之間使用的and進行連接,QueryWrapper中提供and和or方法,可以提供指定的查詢條件的優(yōu)先級。
@Testpublic void testQueryWrapper6(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查詢年齡在20到30之間并且(姓名包含字母a或者郵箱不為null的數據)wrapper.between("age",20,30).and(i->i.like("name","a").or().isNotNull("email"));List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:
SELECT id,name,age,email FROM user WHERE (age BETWEEN ? AND ? AND (name LIKE ? OR email IS NOT NULL))
6.8、QueryWrapper實現子查詢
@Testpublic void testQueryWrapper7(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 查詢年齡在20到30之間的所有數據,這里故意采用子查詢的方式完成wrapper.inSql("age","select age from user where age >= 20 and age <= 30");List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:
SELECT id,name,age,email FROM user WHERE (age IN (select age from user where age >= 20 and age <= 30))
6.9、使用UpdateWrapper實現修改
雖然QueryWrapper可以完成修改操作的,但是需要傳遞實體對象,還是有點小麻煩。提供的UpdateWrapper是專門用于完成修改條件和數據封裝
@Testpublic void testUpdateWrapper(){// 創(chuàng)建UpdateWrapper對象,用于構建查詢條件UpdateWrapper<User> wrapper = new UpdateWrapper<>();// 修改name中包含字母a的數據// 設置修改的的條件wrapper.like("name","a");// 設置需要修改的數據wrapper.set("age",22).set("email","abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}
最終生成的sql:
UPDATE user SET age=?,email=? WHERE (name LIKE ?)
6.10、使用condition組裝條件
condition用于在進行條件封裝的時候,判斷某個值,當這個值不為null的時候,會自動添加對應的條件,如果為null,就不會添加條件
簡單說:condition可以動態(tài)根據條件組裝條件
@Testpublic void testQueryWrapper9(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<>();// 根據指定的數據,添加查詢條件,模擬多條件查詢的場景String name = "a";Integer age = 20;String email = null;wrapper.like(StringUtils.isNotBlank(name),"name",name).gt(age!=null , "age",age).eq(StringUtils.isNotBlank(email),"email",email);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:生成的sql中并不包含email,因為email為null
SELECT id,name,age,email FROM user WHERE (name LIKE ? AND age > ?)
6.11、LambdaQueryWrapper
Lambda相關的查詢與修改構造器,針對QueryWrapper和UpdateWrapper在構造添加的時候,需要書寫表的列名進行優(yōu)化,通過Lambda相關的條件構造器,可以將對應的條件采用實體類的實型進行編寫。
@Testpublic void testQueryWrapper10(){// 創(chuàng)建QueryWrapper對象,用于構建查詢條件LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();// 查詢姓名中包含字母o,郵箱不為null,年齡大于等于20的wrapper.like(User::getName,"o").isNotNull(User::getEmail).ge(User::getAge,20);List<User> users = this.userMapper.selectList(wrapper);users.forEach(System.out::println);}
最終生成的sql:生成的sql中并不包含email,因為email為null
SELECT id,name,age,email FROM user WHERE (name LIKE ? AND email IS NOT NULL AND age >= ?)
6.12、LambdaUpdateWrapper
@Testpublic void testUpdateWrapper(){// 創(chuàng)建UpdateWrapper對象,用于構建查詢條件LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();// 修改name中包含字母a的數據// 設置修改的的條件wrapper.like(User::getName,"a");// 設置需要修改的數據wrapper.set(User::getAge,22).set(User::getEmail,"abc@qq.com");int i = this.userMapper.update(null, wrapper);System.out.println(i);}
最終生成的sql:生成的sql中并不包含email,因為email為null
UPDATE user SET age=?,email=? WHERE (name LIKE ?)
7、MyBatis-Plus分頁
7.1、分頁插件的配置和使用
MyBatis-Plus的分頁非常簡單,只需要添加一個配置類即可,而MyBatis-Plus提供的配置是以插件的方式提供。
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 創(chuàng)建攔截器對象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 設置分頁的攔截器,并設置數據庫類型為mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 返回攔截器對象return interceptor;}
}
通過上面代碼的配置,就可以使用分頁功能
@Testpublic void testPagination(){LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.ge(User::getAge,20);Page<User> page = new Page<>(1,3);// 如果沒有條件,第二個參數可以設置為nullthis.userMapper.selectPage(page , wrapper);// 查詢的結果就放在Page對象中// 獲取當前分頁總頁數System.out.println(page.getPages());// 每頁顯示條數,默認 10System.out.println(page.getSize());// 當前頁System.out.println(page.getCurrent());// 當前滿足條件的總數System.out.println(page.getTotal());// 分頁查詢的數據System.out.println(page.getRecords());}
7.2、自定義分頁功能
在實際使用中,難免會出現需要自己書寫sql語句,同時還需要使用分頁功能,這時就需要自定義分頁功能
需要注意:
- 自定義Mapper接口中的方法的第一個參數必須是MybatisPlus提供的分頁Page對象。
- 自定義Mapper接口中的方法返回值必須是Page對象
public interface UserMapper extends BaseMapper<User> {@Select("select * from user where age > #{age}")public Page<User> selectUserAndPage(@Param("page") Page<User> page , @Param("age") Integer age);
}
@Testpublic void testPagination2(){Page<User> page = new Page<>(1,3);// 如果沒有條件,第二個參數可以設置為nullthis.userMapper.selectUserAndPage(page , 20);// 查詢的結果就放在Page對象中// 獲取當前分頁總頁數System.out.println(page.getPages());// 每頁顯示條數,默認 10System.out.println(page.getSize());// 當前頁System.out.println(page.getCurrent());// 當前滿足條件的總數System.out.println(page.getTotal());// 分頁查詢的數據System.out.println(page.getRecords());}
7.3、樂觀鎖與悲觀鎖
MySQL中的樂觀鎖和悲觀鎖主要區(qū)別如下:
- 悲觀鎖:
- 悲觀鎖會在更新數據時對數據加鎖,避免其他事務對此數據進行更新。
- 通常使用鎖定的SELECT…FOR UPDATE語句來實現。
- 在整個更新操作過程中,數據被鎖定,無法進行其他更新操作,保證數據consistency。
- 效率低,鎖定時間長,發(fā)生鎖爭用的概率大。
- 樂觀鎖:
- 樂觀鎖不會對數據加鎖,只在更新時檢查版本號,如果版本號不同,則說明數據已經被更新。
- 通常使用數據版本號version來實現。
- 在更新時,通過where 條件檢查version號是否與最新的相同。如果不同就代表已經更新過了。
- 效率高,不會發(fā)生死鎖。
InnoDB存儲引擎從MySQL 5.5開始支持行鎖,可以實現行級別的悲觀鎖。
總體來說,樂觀鎖適用于寫比較少的場景,可以提高吞吐量。悲觀鎖適用于寫比較多的場景,可以保證數據的完整性。
7.4、mybatis-plus的樂觀鎖插件
為了測試,重新創(chuàng)建一張表
CREATE TABLE goods(id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(100),price DOUBLE ,num INT,VERSION INT DEFAULT 0
);
INSERT INTO goods(id,NAME,price,num,VERSION) VALUES(NULL,"華為手機",100,3,0);
需要在數據庫表中添加一列version,用于樂觀鎖的版本號確認,需要在實體類中使用@Version注解。
@Data
@TableName("goods")
public class Goods {@TableIdprivate Long id;private String name;private Integer price;private Integer num;@Versionprivate Integer version;
}
需要在攔截器中添加樂觀鎖的插件
@Configuration
public class MyBatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 創(chuàng)建攔截器對象MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 設置分頁的攔截器,并設置數據庫類型為mysqlinterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));// 添加樂觀鎖的攔截器interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());// 返回攔截器對象return interceptor;}
}
@SpringBootTest
public class GoodsTest {@Autowiredprivate GoodsMapper goodsMapper;@Testpublic void test(){// 架設一個操作:需要對商品的價格先增加50%,然后在優(yōu)惠20%// 架設另一個操作:需要對商品的價格在前一個修改后的基礎上在優(yōu)惠10%// 但是可能會出現兩個操作同時進行的情況,就需要通過樂觀鎖進行控制// 架設兩個操作同時進行,操作之前需要先查詢到當前商品的數據信息Goods goods = this.goodsMapper.selectById(1);Goods goods2 = this.goodsMapper.selectById(1);// 開始執(zhí)行 價格先增加50%goods.setPrice(goods.getPrice()*1.5);// 然后在優(yōu)惠20%goods.setPrice(goods.getPrice()*0.8);// 將數據跟新到數據庫int i = this.goodsMapper.updateById(goods);System.out.println(i);// goods2進行更新goods2.setPrice(goods2.getPrice()*0.9);// goods2更新不會成功this.goodsMapper.updateById(goods2);// 若要更新成功,就必須在更新失敗后,重新獲取數據,對最新的數據進行更新}
}
通過執(zhí)行的過程中產生的sql語句,會發(fā)現,version作為where的條件存在
UPDATE goods SET name=?, price=?, num=?, version=? WHERE id=? AND version=?
8、代碼生成器
MybatisX 是一款基于 IDEA 的快速開發(fā)插件,為效率而生。
安裝:打開 IDEA,進入 File -> Settings -> Plugins -> Browse Repositories,輸入 mybatisx
搜索并安裝。安裝成功會提示重啟idea,我們重慶idea之后,就可以使用MybatisX插件了
使用前提:需要配置數據庫源
選擇 +
按鈕,然后選擇Data Source
菜單,找到 MySql
選項
在彈出窗口中,填寫對應的信息,最后點擊OK
,即可完成數據源的配置
打開數據源,找到對應的表,右擊選擇MyBatisX-Generator
需要根據提示,填寫對應的內容
繼續(xù)完成對應的配置
最后選擇Finish
,即可生成最基礎的模版,然后就可以開始愉快的編程了。
在mybatis-plus默認的功能不夠的時候,就可以借助MyBaitsX快速給Mapper接口添加對應的方法,同時也會在mapper文件中生成對應的sql 語句。