目錄
什麼是mybatis-plus
MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。
特點
- 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
- 損耗小:啓動即會自動注入基本 CURD,性能基本無損耗,直接面向對象操作
- 強大的 CRUD 操作:內置通用 Mapper、通用 Service,僅僅通過少量配置即可實現單表大部分 CRUD 操作,更有強大的條件構造器,滿足各類使用需求
- 支持 Lambda 形式調用:通過 Lambda 表達式,方便的編寫各類查詢條件,無需再擔心字段寫錯
- 支持主鍵自動生成:支持多達 4 種主鍵策略(內含分佈式唯一 ID 生成器 - Sequence),可自由配置,完美解決主鍵問題
- 支持 ActiveRecord 模式:支持 ActiveRecord 形式調用,實體類只需繼承 Model 類即可進行強大的 CRUD 操作
- 支持自定義全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 內置代碼生成器:採用代碼或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 層代碼,支持模板引擎,更有超多自定義配置等您來使用
- 內置分頁插件:基於 MyBatis 物理分頁,開發者無需關心具體操作,配置好插件之後,寫分頁等同於普通 List 查詢
- 分頁插件支持多種數據庫:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多種數據庫
- 內置性能分析插件:可輸出 Sql 語句以及其執行時間,建議開發測試時啓用該功能,能快速揪出慢查詢
- 內置全局攔截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規則,預防誤操作
框架結構
引入依賴
springboot
maven:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
gradle:
compile group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.3.1.tmp'
springmvc
maven:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.3.1.tmp</version>
</dependency>
gradle:
compile group: 'com.baomidou', name: 'mybatis-plus', version: '3.3.1.tmp'
代碼生成器
代碼生成器可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。
還沒有試用代碼生成器之前的項目結構:
數據庫設置
1.新建數據庫:test
2.新建數據表:users,數據表結構如下:
代碼開發
引入依賴,這裏我一maven爲例,gradle可以搜索相關依賴:
<?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.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ymy</groupId>
<artifactId>springboot-mybatis-plus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-mybatis-plus</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!--簡化bean代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis-plus依賴-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<!--代碼生成器 生產環境中可以不用引入-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.1.tmp</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.30</version>
</dependency>
<!--測試依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
CodeGenerator代碼生成器類:
package com.ymy.config;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
public class CodeGenerator {
public static void main(String[] args) {
// 代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("F:\\demo\\springboot-mybatis-plus\\src\\main\\java");
gc.setAuthor("yaomaoyang");
gc.setOpen(false);
// gc.setSwagger2(true); 實體屬性 Swagger2 註解
mpg.setGlobalConfig(gc);
// 數據源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://127.0.0.1:33306/test?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
//pc.setModuleName(scanner("模塊名"));
pc.setParent("com.ymy");
mpg.setPackageInfo(pc);
// 自定義配置
// InjectionConfig cfg = new InjectionConfig() {
// @Override
// public void initMap() {
// Map<String, Object> map = new HashMap<>();
// map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
// this.setMap(map);
// }
// };
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定義輸出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定義配置會被優先輸出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定義輸出文件名 , 如果你 Entity 設置了前後綴、此處注意 xml 的名稱會跟着發生變化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
// cfg.setFileCreate(new IFileCreate() {
// @Override
// public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// // 判斷自定義文件夾是否需要創建
// checkDir("調用默認方法創建的目錄");
// return false;
// }
// });
// cfg.setFileOutConfigList(focList);
// mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定義輸出模板
//指定自定義模板路徑,注意不要帶上.ftl/.vm, 會根據使用的模板引擎自動識別
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
//strategy.setSuperEntityClass("你自己的父類實體,沒有就不用設置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父類
// strategy.setSuperControllerClass("你自己的父類控制器,沒有就不用設置!");
// 寫於父類中的公共字段
//strategy.setSuperEntityColumns("id");
strategy.setInclude("users");//表名
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
運行main方法:
10:01:54.232 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================準備生成文件...==========================
10:01:54.719 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\entity]
10:01:54.719 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\controller]
10:01:54.720 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service]
10:01:54.721 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\mapper]
10:01:54.721 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 創建目錄: [F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\impl]
10:01:54.992 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/entity.java.ftl; 文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\entity\Users.java
10:01:55.010 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/mapper.java.ftl; 文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\mapper\UsersMapper.java
10:01:55.016 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/service.java.ftl; 文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\IUsersService.java
10:01:55.026 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/serviceImpl.java.ftl; 文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\service\impl\UsersServiceImpl.java
10:01:55.034 [main] DEBUG com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine - 模板:/templates/controller.java.ftl; 文件:F:\demo\springboot-mybatis-plus\src\main\java\com\ymy\controller\UsersController.java
10:01:55.034 [main] DEBUG com.baomidou.mybatisplus.generator.AutoGenerator - ==========================文件生成完成!!!==========================
Process finished with exit code 0
看到上面的標誌表示代碼已經生成完畢,項目目錄結構如下:
由於後續的開發我都將採用註解的形式,所以我這裏並沒有生成xml文件,如果你想生成xml文件,可以設置這個文件:
templateConfig.setXml(null);
使用mybatis自帶的方法進行增刪改查
數據源配置
打開application.yml配置文件
spring:
datasource:
url: jdbc:mysql://127.0.0.1:33306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&allowMultiQueries=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
注意: driver-class-name 這個參數值回會根據mysql驅動的版本變動而變動。
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
如果mysql-connector-java版本大於6.0,那麼使用:com.mysql.cj.jdbc.Driver,否則:com.mysql.jdbc.Driver,這點需要特別注意。
1.新增
由於新增操作時針對對象操作,所以我對Users,請看代碼:
如果你引入了@Builder這種建造者模式,那麼Users對象將不能使用new進行實例化,如果想支持建造者和new兩種創建實例的方式,那麼需要引入兩個註解:@NoArgsConstructor(無參構造)、@AllArgsConstructor(有參構造,這個看情況)。
這裏還需要注意一點,u_sex、deleted兩個字段類型爲tinyint(1),使用代碼生成器生成的實體類Users中與他們對應的類型是:boolean,所以這裏最好將他們修改爲Integer。
新增測試類:MybatisPlusTest.java
package com.ymy;
import com.ymy.entity.Users;
import com.ymy.service.IUsersService;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.time.LocalDateTime;
@SpringBootTest
@Slf4j
public class MybatisPlusTest {
@Autowired
private IUsersService usersService;
/**
* 插入操作(使用mybatis-plus自帶方法)
*/
@Test
public void add(){
//初始化實例並賦值
Users user = Users.builder().uName("張三").uMail("[email protected]")
.uPassword("123456").uPhone("15000000000")
.createTime(LocalDateTime.now()).uSex(1).deleted(0).build();
//插入
boolean save = usersService.save(user);
log.info("保存返回的結果:{}",save);
}
}
運行add方法:
數據庫:
查詢
由於查詢會涉及到列表,剛剛直插入了一條,這裏我在數據庫手動添加了3條數據:
查詢代碼:
/**
* 查詢操作(使用mybatis-plus自帶方法)
*/
@Test
public void select(){
//查詢整張表
List<Users> list = usersService.list();
log.info("查詢整張表的數據結果:{}",list);
//通過用戶id查詢用戶
Users user = usersService.getById(1);
log.info("通過用戶id查詢數據結果:{}",user);
}
查詢結果:
2020-03-10 10:54:39.790 INFO 56092 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 10:54:40.011 INFO 56092 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 10:54:40.059 INFO 56092 --- [ main] com.ymy.MybatisPlusTest : 查詢整張表的數據結果:[Users(id=1, uName=張三, uPassword=123456, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:44:15, deleted=0), Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0), Users(id=3, uName=王五, uPassword=123456, [email protected], uPhone=15222222222, uSex=1, createTime=2020-03-10T10:48:07, deleted=0), Users(id=4, uName=趙六, uPassword=123456, [email protected], uPhone=15333333333, uSex=1, createTime=2020-03-10T10:48:34, deleted=0)]
2020-03-10 10:54:40.067 INFO 56092 --- [ main] com.ymy.MybatisPlusTest : 通過用戶id查詢數據結果:Users(id=1, uName=張三, uPassword=123456, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)
修改操作
/**
* 修改操作(使用mybatis-plus自帶方法)
*/
@Test
public void update(){
Users user = Users.builder().id(1).uName("張三").uMail("[email protected]")
.uPassword("123123").uPhone("15000000000")
.createTime(LocalDateTime.now()).uSex(1).deleted(0).build();
//通過id修改數據
boolean flag = usersService.updateById(user);
log.info("根據id修改數據返回的結果:{}",flag);
}
刪除操作
/**
* 刪除操作(使用mybatis-plus自帶方法)
*/
@Test
public void remove(){
//通過id刪除記錄
boolean flag = usersService.removeById(4);
log.info("根據id修改數據返回的結果:{}",flag);
}
條件構造
條件構造可以讓sql變得更加的靈活,不用寫代碼也可以操作多條件的sql語句,下面我們一起來看看條件構造的使用方式:
AbstractWrapper
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父類用於生成 sql 的 where 條件, entity 屬性也用於生成 sql 的 where 條件注意: entity 生成的 where 條件與 使用各個 api 生成的 where 條件沒有任何關聯行爲
請看代碼實例:
/**
* 條件構造
*/
@Test
public void conditionalConstruct(){
//通過用戶名和性別查詢用戶信息
QueryWrapper<Users> usersQueryWrapper = new QueryWrapper<Users>()
.eq("u_name","張三")//姓名
.eq("u_sex",1);//性別
List<Users> users = usersService.list(usersQueryWrapper);
log.info("根據用戶名和性別查詢的結果:{}",users);
//將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999
UpdateWrapper<Users> updateWrapper = new UpdateWrapper<Users>()
.eq("u_name","張三")//姓名
.eq("u_sex",1)
.set("u_password","999999");//將密碼設置爲999999
boolean update = usersService.update(updateWrapper);
log.info("將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999的返回結果:{}",update);
//通過對象類的實行修改
UpdateWrapper<Users> updateEntity = new UpdateWrapper<Users>()
.eq("u_name","張三")//姓名
.eq("u_sex",1);
Users users1 = new Users();
users1.setUPassword("999999");
boolean update1 = usersService.update(users1, updateEntity);
log.info("通過對象類的實行修改返回的結果:{}",update1);
//刪除用戶名爲王五並且性別爲1的用戶
UpdateWrapper<Users> deleteUser = new UpdateWrapper<Users>()
.eq("u_name","王五")//姓名
.eq("u_sex",1);
boolean remove = usersService.remove(deleteUser);
log.info("刪除用戶名爲王五並且性別爲1的用戶返回結果:{}",remove);
//lambda表達式查詢 查詢用戶名爲zhangsan的用戶
LambdaQueryWrapper<Users> lambdaWrapper = new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三");
List<Users> lambdaUsers = usersService.list(lambdaWrapper);
log.info("lambda表達式查詢 查詢用戶名爲zhangsan的用戶返回結果:{}",lambdaUsers);
}
2020-03-10 11:25:36.666 INFO 78972 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 11:25:36.901 INFO 78972 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 11:25:36.945 INFO 78972 --- [ main] com.ymy.MybatisPlusTest : 根據用戶名和性別查詢的結果:[Users(id=1, uName=張三, uPassword=123123, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T02:58:19, deleted=0)]
2020-03-10 11:25:36.966 INFO 78972 --- [ main] com.ymy.MybatisPlusTest : 將用戶名爲張三並且性別爲男的用戶的密碼設置爲999999的返回結果:true
2020-03-10 11:25:36.982 INFO 78972 --- [ main] com.ymy.MybatisPlusTest : 通過對象類的實行修改返回的結果:true
2020-03-10 11:25:36.998 INFO 78972 --- [ main] com.ymy.MybatisPlusTest : 刪除用戶名爲王五並且性別爲1的用戶返回結果:true
2020-03-10 11:25:37.019 INFO 78972 --- [ main] com.ymy.MybatisPlusTest : lambda表達式查詢 查詢用戶名爲zhangsan的用戶返回結果:[Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)]
以上我只寫了一個和條件構造一起使用的條件:eq,還有很多,詳情請參https://mp.baomidou.com/guide/wrapper.html#abstractwrapper
分頁插件
mybatis-plus使用分頁不需要像mybatis一樣添加依賴,因爲mybatis-plus已經提供了分頁插件,直接拿來使用即可。
1.引入依賴:
package com.ymy.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.optimize.JsqlParserCountOptimize;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 設置請求的頁面大於最大頁後操作, true調回到首頁,false 繼續請求 默認false
// paginationInterceptor.setOverflow(false);
// 設置最大單頁限制數量,默認 500 條,-1 不受限制
// paginationInterceptor.setLimit(500);
// 開啓 count 的 join 優化,只針對部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
打工告成,接下來就可以使用分頁了。
/**
* 分頁測試
*/
@Test
public void paginationTest(){
//分頁查詢
Page<Users> page = new Page<Users>(1,5);// 1:當前頁 5:每頁展示多少行
IPage<Users> usersPage = usersService.page(page);
log.info("總數:{} 條",usersPage.getTotal());
log.info("通過分頁查詢返回的結果條數:{}",usersPage.getRecords().size());
log.info("通過分頁查詢返回數據:{}",usersPage);
for(Users user: usersPage.getRecords()){
log.info("記錄:{}",user);
}
}
2020-03-10 11:54:23.113 INFO 83360 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 11:54:23.356 INFO 83360 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 總數:6 條
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回的結果條數:5
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@7da34b26
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)
2020-03-10 11:54:23.463 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=5, uName=王五, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T03:41:12, deleted=0)
2020-03-10 11:54:23.464 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=6, uName=趙六, uPassword=123456, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:13, deleted=0)
2020-03-10 11:54:23.464 INFO 83360 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=7, uName=王麻子, uPassword=123123, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:31, deleted=0)
將當前頁換成2
2020-03-10 12:01:24.239 INFO 82440 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 12:01:24.467 INFO 82440 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 12:01:24.571 INFO 82440 --- [ main] com.ymy.MybatisPlusTest : 總數:6 條
2020-03-10 12:01:24.572 INFO 82440 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回的結果條數:1
2020-03-10 12:01:24.572 INFO 82440 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@55fee662
2020-03-10 12:01:24.572 INFO 82440 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=8, uName=令狐沖, uPassword=110110, [email protected], uPhone=15888888888, uSex=0, createTime=2020-03-10T03:41:52, deleted=0)
上面的查詢沒有嵌套條件,我們也可以結合查詢條件一起使用,mybatis-plus也給出了對應的方法,請看源碼:
/**
* 翻頁查詢
*
* @param page 翻頁對象
* @param queryWrapper 實體對象封裝操作類 {@link com.baomidou.mybatisplus.core.conditions.query.QueryWrapper}
*/
default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
return getBaseMapper().selectPage(page, queryWrapper);
}
分頁結合條件構造一起使用,由於前面已經講解過條件構造了,這裏就不做代碼展示了,你可能還有疑問,那就是使用mybatis-plus自帶的方法可以使用這個分頁插件,如果是我們自己手動寫的sql,也能支持分頁嗎?,當然可以,請看代碼:
/**
* 分頁測試(手動編寫sql)
*/
@Test
public void paginationSqlTest(){
//分頁查詢
Page<Users> page = new Page<Users>(1,5);// 1:當前頁 5:每頁展示多少行
IPage<Users> usersPage = usersService.selectList(page);
log.info("總數:{} 條",usersPage.getTotal());
log.info("通過分頁查詢返回的結果條數:{}",usersPage.getRecords().size());
log.info("通過分頁查詢返回數據:{}",usersPage);
for(Users user: usersPage.getRecords()){
log.info("記錄:{}",user);
}
}
public interface IUsersService extends IService<Users> {
/**
* 通過手動編寫sql查詢分頁
* @param page
* @return
*/
IPage<Users> selectList(Page<Users> page);
}
@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {
@Autowired
private UsersMapper usersMapper;
@Override
public IPage<Users> selectList(Page<Users> page) {
return usersMapper.selectListBySql(page);
}
}
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
@Select("select * from users ")
IPage<Users> selectListBySql(Page<Users> page);
}
2020-03-10 12:08:23.945 INFO 85452 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 12:08:24.173 INFO 85452 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 12:08:24.282 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 總數:6 條
2020-03-10 12:08:24.282 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回的結果條數:5
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 通過分頁查詢返回數據:com.baomidou.mybatisplus.extension.plugins.pagination.Page@73a8e994
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T03:25:39, deleted=0)
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=2, uName=李四, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T02:44:15, deleted=0)
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=5, uName=王五, uPassword=123456, [email protected], uPhone=15111111111, uSex=1, createTime=2020-03-10T03:41:12, deleted=0)
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=6, uName=趙六, uPassword=123456, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:13, deleted=0)
2020-03-10 12:08:24.283 INFO 85452 --- [ main] com.ymy.MybatisPlusTest : 記錄:Users(id=7, uName=王麻子, uPassword=123123, [email protected], uPhone=15111111111, uSex=0, createTime=2020-03-10T03:41:31, deleted=0)
可以看出,手動編寫的sql,同樣是支持分頁的,查詢也很簡單,只需要將Page對象傳遞到Mapper即可,我們一起來看看返回的對象IPage到底是一個什麼東西。
public interface IPage<T> extends Serializable {
/**
* 降序字段數組
*
* @return order by desc 的字段數組
* @see #orders()
*/
@Deprecated
default String[] descs() {
return null;
}
/**
* 升序字段數組
*
* @return order by asc 的字段數組
* @see #orders()
*/
@Deprecated
default String[] ascs() {
return null;
}
/**
* 獲取排序信息,排序的字段和正反序
*
* @return 排序信息
*/
List<OrderItem> orders();
/**
* KEY/VALUE 條件
*
* @return ignore
*/
default Map<Object, Object> condition() {
return null;
}
/**
* 自動優化 COUNT SQL【 默認:true 】
*
* @return true 是 / false 否
*/
default boolean optimizeCountSql() {
return true;
}
/**
* 進行 count 查詢 【 默認: true 】
*
* @return true 是 / false 否
*/
default boolean isSearchCount() {
return true;
}
/**
* 計算當前分頁偏移量
*/
default long offset() {
return getCurrent() > 0 ? (getCurrent() - 1) * getSize() : 0;
}
/**
* 當前分頁總頁數
*/
default long getPages() {
if (getSize() == 0) {
return 0L;
}
long pages = getTotal() / getSize();
if (getTotal() % getSize() != 0) {
pages++;
}
return pages;
}
/**
* 內部什麼也不幹
* <p>只是爲了 json 反序列化時不報錯</p>
*/
default IPage<T> setPages(long pages) {
// to do nothing
return this;
}
/**
* 設置是否命中count緩存
*
* @param hit 是否命中
* @since 3.3.1
*/
default void hitCount(boolean hit) {
}
/**
* 是否命中count緩存
*
* @return 是否命中count緩存
* @since 3.3.1
*/
default boolean isHitCount() {
return false;
}
/**
* 分頁記錄列表
*
* @return 分頁對象記錄列表
*/
List<T> getRecords();
/**
* 設置分頁記錄列表
*/
IPage<T> setRecords(List<T> records);
/**
* 當前滿足條件總行數
*
* @return 總條數
*/
long getTotal();
/**
* 設置當前滿足條件總行數
*/
IPage<T> setTotal(long total);
/**
* 獲取每頁顯示條數
*
* @return 每頁顯示條數
*/
long getSize();
/**
* 設置每頁顯示條數
*/
IPage<T> setSize(long size);
/**
* 當前頁,默認 1
*
* @return 當前頁
*/
long getCurrent();
/**
* 設置當前頁
*/
IPage<T> setCurrent(long current);
/**
* IPage 的泛型轉換
*
* @param mapper 轉換函數
* @param <R> 轉換後的泛型
* @return 轉換泛型後的 IPage
*/
@SuppressWarnings("unchecked")
default <R> IPage<R> convert(Function<? super T, ? extends R> mapper) {
List<R> collect = this.getRecords().stream().map(mapper).collect(toList());
return ((IPage<R>) this).setRecords(collect);
}
}
邏輯刪除
什麼是邏輯刪除?在刪除中,分物理刪除和邏輯刪除,物理刪除:將數據從數據表中刪除;邏輯刪除:使用一個字段標識,未被刪除是一個狀態,刪除以後是一個狀態,說直白一點,邏輯刪除就是一個更新操作。
準備工作
在用戶表新建一個字段用來標識用戶的狀態:deleted(是否刪除 0:正常 1:刪除),由於之前新建users表中已經添加了此字段,所以我們直接開始寫代碼。
編輯配置文件application.yml
# 邏輯刪除
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
在實體類Users中設置狀態的字段
/**
* 是否刪除 0:正常 1:刪除
*/
@TableLogic
private Integer deleted;
新建測試代碼
/**
* 邏輯刪除
*/
@Test
public void logicRemove(){
//刪除用戶名爲令狐沖的用戶信息
boolean remove = usersService.remove(new UpdateWrapper<Users>().lambda().eq(Users::getUName, "令狐沖"));
log.info("刪除返回的結果:{}",remove);
}
可以發現,令狐沖的deleted狀態被設置爲了1,不知道有沒有細心的靚仔發現了一個問題,那就是我已經在配置文件中設置了logic-delete-field: deleted:全局邏輯刪除字段值,爲什麼還要在實體類中加入 @TableLogic註解呢?
logic-delete-field代表全局配置,如果你設置了這個屬性,那麼系統會自動在所有實體類中匹配你設置的字段,如果沒有找到,執行物理刪除,@TableLogic可以在不同的實體類設置在不同的字段上,如果全局配置和註解都沒有,那麼將會執行物理刪除,註解的形式會相對來說比較靈活,但是代碼侵入較強,全局配置比較死板,但是代碼侵入較弱,項目使用中可以根據自己數據表的結構做出相應的選擇。
注意:邏輯刪除默認只會在MP自帶的方法中生效,比如查詢:list()方法他會自動加上deleted=0的條件,如果sql時自己編寫的時候Mp不會加上deleted=0的條件,所以在使用自定義sql的時候別忘了加上deleted條件。
通用枚舉類
通用枚舉解決了字段轉換的問題,如:性別,數據庫存放的是數字,但是在界面上卻要展示字符串,我們還需要在程序中做一次轉換,使用MP的通用枚舉類之後,就無需我們自己做轉換了。
測試方法
/**
* 枚舉
*/
@Test
public void enumTest(){
//刪除用戶名爲令狐沖的用戶信息
Users users = usersService.getOne(new UpdateWrapper<Users>().lambda().eq(Users::getUName, "令狐沖"));
log.info("查詢返回的結果:{}",users);
}
這只是一個簡單的查詢列表語句,還沒有設置通用枚舉,我們先看一下沒有設置通用枚舉的結果:
2020-03-10 17:04:04.831 INFO 94084 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-10 17:04:05.091 INFO 94084 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2020-03-10 17:04:05.158 INFO 94084 --- [ main] com.ymy.MybatisPlusTest : 查詢返回的結果:Users(id=1, uName=張三, uPassword=999999, [email protected], uPhone=15000000000, uSex=1, createTime=2020-03-10T06:21:05, deleted=0)
你會發現這時候我們返回的uSex並不是我們需要的結果,我們需要的是男性或者女性,但是程序給我們返回了 1/2,我們需要自己處理,現在我們使用MP的通用枚舉,代碼改造開始。
1.新建性別枚舉類
package com.ymy.entity.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.baomidou.mybatisplus.core.enums.IEnum;
/**
* 性別枚舉類
*/
public enum SexEnum implements IEnum<Integer> {
MAN(1,"男"),
WOMAN(2,"女");
@EnumValue
private final int code;
private final String descp;
SexEnum(int code, String descp) {
this.code = code;
this.descp = descp;
}
@Override
public String toString() {
return this.descp;
}
@Override
public Integer getValue() {
return code;
}
}
2.改造實體類中的性別字段
@JSONField(serialzeFeatures= SerializerFeature.WriteEnumUsingToString)
private SexEnum uSex;
@JSONField需要FastJson的依賴。
3.引入FastJson
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
4.編輯配置文件:application.yml
# 邏輯刪除
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
type-enums-package: com.ymy.entity.enums #掃描MP的通用枚舉類所在的package
運行測試案例,查看結果
這裏我們需要注意一點,如果我們數據庫給定字段的類型如果是tinyint(1),那麼執行上面得到的性別將會時null,原因時MP會將 tinyint(1)映射爲boolean類型,也就是說我們需要將枚舉類中的1,2,改爲true、false,但0一般標識true,非0標識false,true、false無法滿足我們的需求,納入和處理呢?其實很簡單,我們只需要將數據庫的字段類型修改爲int即可。
自動填充功能
自動填充:指定字段在沒有給定具體值時,系統將自動的填充設置好的數據,常見的例子比如數據添加的創建時間,數據修改的修改時間,這些都可以設置爲自動填充。
這個時候我們需要在在數據表增加兩個一個字段,update_time,不能給定默認值,並且不會雖數據修改發生變化。
1.自定義實現通用填充類MyMetaObjectHandler
package com.ymy.config;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
@Slf4j
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
log.info("start insert fill ....");
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用)
// this.fillStrategy(metaObject, "createTime", LocalDateTime.now()); // 也可以使用(3.3.0 該方法有bug請升級到之後的版本如`3.3.1.8-SNAPSHOT`)
/* 上面選其一使用,下面的已過時(注意 strictInsertFill 有多個方法,詳細查看源碼) */
//this.setFieldValByName("operator", "Jerry", metaObject);
//this.setInsertFieldValByName("operator", "Jerry", metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
log.info("start update fill ....");
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); // 起始版本 3.3.0(推薦使用)
//this.fillStrategy(metaObject, "updateTime", LocalDateTime.now()); // 也可以使用(3.3.0 該方法有bug請升級到之後的版本如`3.3.1.8-SNAPSHOT`)
/* 上面選其一使用,下面的已過時(注意 strictUpdateFill 有多個方法,詳細查看源碼) */
//this.setFieldValByName("operator", "Tom", metaObject);
//this.setUpdateFieldValByName("operator", "Tom", metaObject);
}
}
2.在實體類中將需要填充的字段添加註解標識
/**
* 創建時間
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改時間
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
public enum FieldFill {
/**
* 默認不處理
*/
DEFAULT,
/**
* 插入填充字段
*/
INSERT,
/**
* 更新填充字段
*/
UPDATE,
/**
* 插入和更新填充字段
*/
INSERT_UPDATE
}
3.測試案例
/**
* 通用字段測試(插入)
*/
@Test
public void GeneralFieldInsertTest(){
Users user = Users.builder().uName("東方不敗").uMail("[email protected]")
.uPassword("123456").uPhone("15000000000")
.uSex(SexEnum.MAN).deleted(0)
.build();
//插入
boolean save = usersService.save(user);
log.info("保存返回的結果:{}",save);
}
/**
* 通用字段測試(修改)
*/
@Test
public void GeneralFieldUpdateTest(){
Users user = new Users();
user.setUPassword("qoqoqoqoqoqoqo");
//插入
boolean save = usersService.update(user,new UpdateWrapper<Users>().lambda().eq(Users::getUName,"東方不敗"));
log.info("保存返回的結果:{}",save);
}
4.測試結果
添加:
修改
這樣就實現了代碼的自動填充,但是這裏需要注意一個問題,只有使用Mp自帶方法的時候纔會生效,並且使用還是有條件的:必須是update(實體類,Wapper條件),否者自動填充將無效。
樂觀鎖
主要適用場景
意圖:
當要更新一條記錄的時候,希望這條記錄沒有被別人更新
樂觀鎖實現方式:
取出記錄時,獲取當前version
更新時,帶上這個version
執行更新時, set version = newVersion where version = oldVersion
如果version不對,就更新失敗
1.數據庫增加version字段
2.註冊樂觀鎖bean
/**
* 註冊樂觀鎖
* @return
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
3.在Users實體類中添加version,字段,並加上@version註解,標識使用此字段實現樂觀鎖。
/**
* 版本號
*/
@Version
private Long version;
樂觀鎖配置完成,接下來我們將用樂觀鎖兩個事例,一個成功、一個失敗,爲了更好的看到MP所做的操作,我先將sql日誌打印在控制檯中,我們修改配置文件
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted #全局邏輯刪除字段值 3.3.0開始支持,詳情看下面。
logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
type-enums-package: com.ymy.entity.enums #掃描MP的通用枚舉類所在的package
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #控制檯輸出sql
現在我們開始寫測試案例,我們需要將張三的密碼修改爲"yyyyyyyy"。
/**
* 樂觀鎖測試
*/
@Test
public void versionTest() {
Users users = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
users.setUPassword("yyyyyyyy");
boolean update = usersService.updateById(users);
log.info("修改了用戶信息,狀態:{}", update);
}
由於數據庫中version字段是剛剛新增的,並且默認0,我們一起來執行一下測試代碼會發生什麼
我們會發現,MP在條件中自動給我們加上了version字段和deleted,deleted是我們之前配置的邏輯刪除,version是我們剛剛配置的樂觀鎖,可能你現在有疑問了,配置這個version字段的用意到底是什麼呢?我舉個例子,假如你的賬戶有100元,這個時候你想給你的朋友轉50元感謝他幫你搶了一個N95口罩,但是這個時候你的媽媽擔心你沒有錢生活了,他往你的賬戶上轉了1000元,按照我們的預期,現在你的賬戶的餘額應該是:1050元,這是我們預期的,但並不代表這是程序執行後的結果,爲什麼呢?因爲有cpu緩存以及多線程的存在,導致你的賬戶餘額可能是1100,也有可能是1050,也有可能是50;這種結果是不是很可怕,這裏我就不演示這種案例,你只要明白就行,而樂觀鎖就是用來保證你給你朋友轉賬/你媽媽給你轉賬只能成功一個,這樣就保證了你資金的安全,現在我們來模擬一個修改用戶信息失敗的案例,請看代碼:
/**
* 樂觀鎖測試
*/
@Test
public void versionTest() throws InterruptedException {
//線程1
Thread t1 = new Thread(() -> {
updatePwd1();
});
//線程2
Thread t2 = new Thread(() -> {
updatePwd2();
});
t1.start();
t2.start();
t1.join();
t2.join();
log.info("執行完畢");
}
/**
* 將張三的密碼修改爲:pppppppppppp
*
* @return
*/
private void updatePwd1() {
String threadName = Thread.currentThread().getName();
Users users = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
log.info("線程:{} 獲取到的用戶信息:{}", threadName, users);
try {
//爲了能讓兩個查詢都執行完畢之後在執行修改操作,所以在這裏休眠了1秒中
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
users.setUPassword("pppppppppppp");
boolean update = usersService.updateById(users);
log.info("線程:{} 修改了用戶信息,狀態:{}", threadName, update);
}
/**
* 將張三的密碼修改爲:ooooooooooo
*
* @return
*/
private void updatePwd2() {
String threadName = Thread.currentThread().getName();
Users users = usersService.getOne(new QueryWrapper<Users>().lambda().eq(Users::getUName, "張三"));
log.info("線程:{} 獲取到的用戶信息:{}", threadName, users);
try {
//爲了能讓兩個查詢都執行完畢之後在執行修改操作,所以在這裏休眠了1秒中
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
users.setUPassword("ooooooooooo");
boolean update = usersService.updateById(users);
log.info("線程:{} 修改了用戶信息,狀態:{}", threadName, update);
}
第一個線程需要將張三的密碼修改爲:pppppppppppp,第二個線程需要將密碼修改爲:ooooooooooo,兩個線程同時執行,我這裏爲了保證兩個線程同時執行查詢,並且都還沒執行修改操作,我這裏就是模擬一下同時修改,你們項目開發的時候千萬別這麼寫代碼,由於我們之前已經執行了一次更新操作,我們的數據庫中version字段的值也應該+1
這時候我們再來運行多線程的測試代碼:
Creating a new SqlSession
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@709bb28d] was not registered for synchronization because synchronization is not active
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21152fe7] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:53.870 INFO 115848 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2020-03-11 10:41:54.128 INFO 115848 --- [ Thread-3] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
JDBC Connection [HikariProxyConnection@1688741162 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
==> Preparing: SELECT id,u_name,u_password,u_mail,u_phone,u_sex,version,create_time,update_time,deleted FROM users WHERE deleted=0 AND (u_name = ?)
==> Parameters: 張三(String)
<== Columns: id, u_name, u_password, u_mail, u_phone, u_sex, version, create_time, update_time, deleted
<== Row: 1, 張三, yyyyyyyy, [email protected], 15000000000, 1, 1, 2020-03-08 06:21:05, 2020-03-09 09:21:10, 0
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@21152fe7]
JDBC Connection [HikariProxyConnection@1306964427 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
2020-03-11 10:41:54.209 INFO 115848 --- [ Thread-3] com.ymy.MybatisPlusTest : 線程:Thread-3 獲取到的用戶信息:Users(id=1, uName=張三, uPassword=yyyyyyyy, [email protected], uPhone=15000000000, uSex=男, version=1, createTime=2020-03-08T06:21:05, updateTime=2020-03-09T09:21:10, deleted=0)
==> Preparing: SELECT id,u_name,u_password,u_mail,u_phone,u_sex,version,create_time,update_time,deleted FROM users WHERE deleted=0 AND (u_name = ?)
==> Parameters: 張三(String)
<== Columns: id, u_name, u_password, u_mail, u_phone, u_sex, version, create_time, update_time, deleted
<== Row: 1, 張三, yyyyyyyy, [email protected], 15000000000, 1, 1, 2020-03-08 06:21:05, 2020-03-09 09:21:10, 0
<== Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@709bb28d]
2020-03-11 10:41:54.221 INFO 115848 --- [ Thread-2] com.ymy.MybatisPlusTest : 線程:Thread-2 獲取到的用戶信息:Users(id=1, uName=張三, uPassword=yyyyyyyy, [email protected], uPhone=15000000000, uSex=男, version=1, createTime=2020-03-08T06:21:05, updateTime=2020-03-09T09:21:10, deleted=0)
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28c0761b] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:55.214 INFO 115848 --- [ Thread-3] com.ymy.config.MyMetaObjectHandler : start update fill ....
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@420ba4aa] was not registered for synchronization because synchronization is not active
2020-03-11 10:41:55.223 INFO 115848 --- [ Thread-2] com.ymy.config.MyMetaObjectHandler : start update fill ....
JDBC Connection [HikariProxyConnection@1719623840 wrapping com.mysql.cj.jdbc.ConnectionImpl@3d6da8bf] will not be managed by Spring
==> Preparing: UPDATE users SET u_name=?, u_password=?, u_mail=?, u_phone=?, u_sex=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? AND deleted=0
JDBC Connection [HikariProxyConnection@2004050803 wrapping com.mysql.cj.jdbc.ConnectionImpl@35e1ffd5] will not be managed by Spring
==> Preparing: UPDATE users SET u_name=?, u_password=?, u_mail=?, u_phone=?, u_sex=?, version=?, create_time=?, update_time=? WHERE id=? AND version=? AND deleted=0
==> Parameters: 張三(String), ooooooooooo(String), [email protected](String), 15000000000(String), 1(Integer), 2(Long), 2020-03-08T06:21:05(LocalDateTime), 2020-03-09T09:21:10(LocalDateTime), 1(Integer), 1(Long)
==> Parameters: 張三(String), pppppppppppp(String), [email protected](String), 15000000000(String), 1(Integer), 2(Long), 2020-03-08T06:21:05(LocalDateTime), 2020-03-09T09:21:10(LocalDateTime), 1(Integer), 1(Long)
<== Updates: 0
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@420ba4aa]
<== Updates: 1
2020-03-11 10:41:55.257 INFO 115848 --- [ Thread-2] com.ymy.MybatisPlusTest : 線程:Thread-2 修改了用戶信息,狀態:false
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@28c0761b]
2020-03-11 10:41:55.258 INFO 115848 --- [ Thread-3] com.ymy.MybatisPlusTest : 線程:Thread-3 修改了用戶信息,狀態:true
2020-03-11 10:41:55.258 INFO 115848 --- [ main] com.ymy.MybatisPlusTest : 執行完畢
這時候你們會發現,線程名:Thread-2修改密碼成功了,Thread-3修改密碼失敗了,符合我們的要求,兩個線程同時執行修改操作的時候,只能成功一個,我們再來看看數據庫的結果:
總結
mybatis-plus的介紹及使用就寫到這裏了,其實還有一些特性我沒有寫到,比如:多租戶 SQL 解析器、動態數據源、自定義ID生成器等等,這篇已經寫的很多了,如果有需要可以評論,我可以繼續加。