Mybatis-Plus超級實用的多種功能用法詳解
一、Mybatis-Plus介紹
1.1、什么是 MyBatis-Plus
MyBatis-Plus 是一款基于 MyBatis 的增強型 ORM 框架,由國內(nèi)團(tuán)隊(苞米豆)開發(fā)維護(hù),遵循「只做增強,不做改變」的核心設(shè)計理念 —— 它完全兼容 MyBatis 的原有功能,同時在其基礎(chǔ)上封裝了大量常用操作,極大簡化了 MyBatis 的開發(fā)流程,減少了重復(fù)的 CRUD 代碼編寫工作。
簡單來說:MyBatis 是「半自動化」ORM 框架(需要手動寫 SQL),而 MyBatis-Plus 在此基礎(chǔ)上,通過內(nèi)置的通用 Mapper/Service,讓開發(fā)者無需編寫基礎(chǔ) SQL 就能完成大部分?jǐn)?shù)據(jù)庫操作,同時保留了 MyBatis 靈活定制 SQL 的能力。
1.2、核心定位
- 增強:對 MyBatis 無侵入式增強,原有 MyBatis 代碼無需修改即可集成。
- 高效:大幅減少 CRUD 代碼、XML 配置的編寫量。
- 靈活:支持自定義 SQL、多數(shù)據(jù)源、插件擴展等高級特性。
1.3、優(yōu)勢
- 無侵入性:完全基于 MyBatis 擴展,不修改 MyBatis 核心源碼,兼容所有 MyBatis 原生功能。
- 強大的 CRUD 封裝:內(nèi)置通用 Mapper/Service,單表操作無需編寫任何 SQL 即可完成增刪改查。
- 支持 Lambda 表達(dá)式:通過 Lambda 語法構(gòu)建查詢條件,避免硬編碼字段名,減少拼寫錯誤。
- 自動分頁:內(nèi)置分頁插件,無需手動處理分頁邏輯,支持多種數(shù)據(jù)庫(MySQL、Oracle、PostgreSQL 等)。
- 主鍵生成策略:內(nèi)置多種主鍵生成策略(雪花算法、自增、UUID 等),可靈活配置。
- 邏輯刪除:無需手動寫刪除邏輯,通過標(biāo)記字段(如
deleted)實現(xiàn)邏輯刪除,保留數(shù)據(jù)。 - 樂觀鎖:內(nèi)置樂觀鎖插件,輕松解決并發(fā)更新問題。
- 代碼生成器:支持快速生成 Entity、Mapper、Service、Controller 等全套代碼,提升開發(fā)效率。
二、Mybatis-Plus快速入門
2.1、引入依賴配置
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>2.2、xml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mp_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
# MyBatis-Plus 配置
mybatis-plus:
# 實體類別名包路徑
type-aliases-package: com.example.mpdemo.entity
# XML映射文件路徑
mapper-locations: classpath:mapper/**/*.xml
# 配置日志(方便調(diào)試SQL)
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 全局配置
global-config:
db-config:
# 主鍵生成策略:雪花算法(適用于分布式)
id-type: ASSIGN_ID
# 邏輯刪除字段名
logic-delete-field: deleted
# 邏輯刪除-未刪除值
logic-not-delete-value: 0
# 邏輯刪除-已刪除值
logic-delete-value: 12.3、實體類
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.time.LocalDateTime;
@Data // Lombok注解,簡化get/set
@TableName("user") // 指定數(shù)據(jù)庫表名(默認(rèn)類名駝峰轉(zhuǎn)下劃線)
public class User {
// 主鍵,使用雪花算法生成
@TableId(type = IdType.AUTO)
private Long id;
// 普通字段(默認(rèn)字段名駝峰轉(zhuǎn)下劃線,如userName -> user_name)
private String userName;
private Integer age;
private String email;
// 邏輯刪除字段(對應(yīng)全局配置的deleted)
@TableLogic
private Integer deleted;
// 自動填充創(chuàng)建時間
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
// 自動填充更新時間
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
}2.4、Mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.mpdemo.entity.User;
import org.apache.ibatis.annotations.Mapper;
// 繼承BaseMapper即可獲得所有CRUD方法,無需編寫任何SQL
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 如需自定義SQL,可在此添加方法并在XML中實現(xiàn)
}2.5、測試
import com.example.mpdemo.entity.User;
import com.example.mpdemo.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest
public class MpDemoTest {
@Autowired
private UserService userService;
// 新增
@Test
public void testAdd() {
User user = new User();
user.setUserName("張三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
userService.save(user);
}
// 查詢所有
@Test
public void testList() {
List<User> userList = userService.list();
userList.forEach(System.out::println);
}
// 條件查詢 + 分頁
@Test
public void testPage() {
// 構(gòu)建分頁條件:第1頁,每頁10條
Page<User> page = new Page<>(1, 10);
// 構(gòu)建查詢條件:年齡大于18
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
.gt(User::getAge, 18);
// 分頁查詢
Page<User> userPage = userService.page(page, wrapper);
// 輸出結(jié)果
System.out.println("總條數(shù):" + userPage.getTotal());
System.out.println("總頁數(shù):" + userPage.getPages());
userPage.getRecords().forEach(System.out::println);
}
}三、Mybatis-Plus的功能使用
3.1、BaseMapper<User>
在mybatis中如果不想寫xml中的表的curd代碼,就可以使用mybatisplus中提供的繼承BaseMapper<User>,然后在mapper接口層中去繼承。
注意:要指定實體類泛型,只有指定了泛型,mybatisplus才知道操作哪個實體的curd,下面我以user實體為例
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
void saveUser(User user);
void deleteUser(Long id);
void updateUser(User user);
User queryUserById(@Param("id") Long id);
List<User> queryUserByIds(@Param("ids") List<Long> ids);
}3.2、常見注解
在mybatisplus中有一些約定如下:
- 在實體類里面,類名駝峰轉(zhuǎn)下劃線作為表名
- 名為id的字段作為主鍵
- 變量名駝峰轉(zhuǎn)下劃線作為表的字段名
如果實體和表的關(guān)系不符合上述mp的約定,那么就需要自己去定義表名和字段名,那么如何去定義呢?答案是
@TableName:用來指定表名,
@TableId用來指定表中的主鍵字段信息,如果不添加type的值,默認(rèn)type=ASSIGN_ID,生成long類型的id
@TableFiled用來指定表中普通字段信息

注意:
- 以屬性名is開頭且是bool類型,就算符合約定也需要加上@TableField主鍵
- 成員變量名與數(shù)據(jù)庫關(guān)鍵字沖突時,要加上@TableField注解,且里面的值要用反引號引起來
- 成員變量不是字段值,需要加上@TableField,且指定exist=false。
3.3、常見配置

注意:配置中id-type如果是assign_id,而類注解上@TableId("",type=IdType.Auto),那么由于注解優(yōu)先級高于全局配置優(yōu)先級,所以type=IdType.Auto會生效,但是如果沒有指定的話,那么全局配置將生效
3.4、條件構(gòu)造器
除了新增以外,修改、刪除、查詢的SQL語句都需要指定where條件。因此mybatis-plus中BaseMapper提供的相關(guān)方法除了以id作為where條件以外,還支持更加復(fù)雜的where條件。

參數(shù)中的Wrapper就是條件構(gòu)造的抽象類,其下有很多默認(rèn)實現(xiàn),繼承關(guān)系如圖:

接下來,我們就來看看如何利用Wrapper實現(xiàn)復(fù)雜查詢。
3.4.1、QueryWrapper
無論是修改、刪除、查詢,都可以使用QueryWrapper來構(gòu)建查詢條件。
接下來看一些例子:
查詢:查詢出名字中帶o的,存款大于等于1000元的人。代碼如下:
@Test
void testQueryWrapper() {
// 1.構(gòu)建查詢條件 where name like "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.select("id", "username", "info", "balance")
.like("username", "o")
.ge("balance", 1000);
// 2.查詢數(shù)據(jù)
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}更新:更新用戶名為jack的用戶的余額為2000,代碼如下:
@Test
void testUpdateByQueryWrapper() {
// 1.構(gòu)建查詢條件 where name = "Jack"
QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "Jack");
// 2.更新數(shù)據(jù),user中非null字段都會作為set語句
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
}3.4.2、UpdateWrapper
基于BaseMapper中的update方法更新時只能直接賦值,對于一些復(fù)雜的需求就難以實現(xiàn)。 例如:更新id為1,2,4的用戶的余額,扣200,對應(yīng)的SQL應(yīng)該是:
UPDATE user SET balance = balance - 200 WHERE id in (1, 2, 4)
SET的賦值結(jié)果是基于字段現(xiàn)有值的,這個時候就要利用UpdateWrapper中的setSql功能了:
@Test
void testUpdateWrapper() {
List<Long> ids = List.of(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.setSql("balance = balance - 200") // SET balance = balance - 200
.in("id", ids); // WHERE id in (1, 2, 4)
// 2.更新,注意第一個參數(shù)可以給null,也就是不填更新字段和數(shù)據(jù),
// 而是基于UpdateWrapper中的setSQL來更新
userMapper.update(null, wrapper);
}3.4.3、LambdaQueryWrapper
無論是QueryWrapper還是UpdateWrapper在構(gòu)造條件的時候都需要寫死字段名稱,會出現(xiàn)字符串魔法值。這在編程規(guī)范中顯然是不推薦的。 那怎么樣才能不寫字段名,又能知道字段名呢?
其中一種辦法是基于變量的gettter方法結(jié)合反射技術(shù)。因此我們只要將條件對應(yīng)的字段的getter方法傳遞給MybatisPlus,它就能計算出對應(yīng)的變量名了。而傳遞方法可以使用JDK8中的方法引用和Lambda表達(dá)式。 因此MybatisPlus又提供了一套基于Lambda的Wrapper,包含兩個:
- LambdaQueryWrapper
- LambdaUpdateWrapper
分別對應(yīng)QueryWrapper和UpdateWrapper
其使用方式如下:
@Test
void testLambdaQueryWrapper() {
// 1.構(gòu)建條件 WHERE username LIKE "%o%" AND balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.lambda()
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000);
// 2.查詢
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}3.5、自定義SQL
先來看一個案例代碼:

這種寫法在一些企業(yè)也是不允許的,因為SQL語句最好都維護(hù)在持久層,而不是業(yè)務(wù)層。就當(dāng)前案例來說,由于條件是in語句,只能將SQL寫在Mapper.xml文件,利用foreach來生成動態(tài)SQL。 這實在是太麻煩了。假如查詢條件更復(fù)雜,動態(tài)SQL的編寫也會更加復(fù)雜。
所以,MybatisPlus提供了自定義SQL功能,可以讓我們利用Wrapper生成查詢條件,再結(jié)合Mapper.xml編寫SQL
@Test
void testCustomWrapper() {
// 1.準(zhǔn)備自定義查詢條件
List<Long> ids = List.of(1L, 2L, 4L);
QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);
// 2.調(diào)用mapper的自定義方法,直接傳遞Wrapper
userMapper.deductBalanceByIds(200, wrapper);
}然后在UserMapper中自定義SQL:
package com.itheima.mp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.mp.domain.po.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Update;
import org.apache.ibatis.annotations.Param;
public interface UserMapper extends BaseMapper<User> {
@Select("UPDATE user SET balance = balance - #{money} ${ew.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("ew") QueryWrapper<User> wrapper);
}3.6、Iservice接口
3.6.1、基本用法
MybatisPlus不僅提供了BaseMapper,還提供了通用的Service接口及默認(rèn)實現(xiàn),封裝了一些常用的service模板方法。 通用接口為IService,默認(rèn)實現(xiàn)為ServiceImpl,其中封裝的方法可以分為以下幾類:
save:新增remove:刪除update:更新get:查詢單個結(jié)果list:查詢集合結(jié)果count:計數(shù)page:分頁查詢
由于Service中經(jīng)常需要定義與業(yè)務(wù)有關(guān)的自定義方法,因此我們不能直接使用IService,而是自定義Service接口,然后繼承IService以拓展方法。同時,讓自定義的Service實現(xiàn)類繼承ServiceImpl,這樣就不用自己實現(xiàn)IService中的接口了。
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
public interface IUserService extends IService<User> {
// 拓展自定義方法
}package com.itheima.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.service.IUserService;
import com.itheima.mp.mapper.UserMapper;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements IUserService {
}測試代碼:
package com.itheima.mp.controller;
import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "用戶管理接口")
@RequiredArgsConstructor
@RestController
@RequestMapping("users")
public class UserController {
private final IUserService userService;
@PostMapping
@ApiOperation("新增用戶")
public void saveUser(@RequestBody UserFormDTO userFormDTO){
// 1.轉(zhuǎn)換DTO為PO
User user = BeanUtil.copyProperties(userFormDTO, User.class);
// 2.新增
userService.save(user);
}
@DeleteMapping("/{id}")
@ApiOperation("刪除用戶")
public void removeUserById(@PathVariable("id") Long userId){
userService.removeById(userId);
}
@GetMapping("/{id}")
@ApiOperation("根據(jù)id查詢用戶")
public UserVO queryUserById(@PathVariable("id") Long userId){
// 1.查詢用戶
User user = userService.getById(userId);
// 2.處理vo
return BeanUtil.copyProperties(user, UserVO.class);
}
@GetMapping
@ApiOperation("根據(jù)id集合查詢用戶")
public List<UserVO> queryUserByIds(@RequestParam("ids") List<Long> ids){
// 1.查詢用戶
List<User> users = userService.listByIds(ids);
// 2.處理vo
return BeanUtil.copyToList(users, UserVO.class);
}
}不過,一些帶有業(yè)務(wù)邏輯的接口則需要在service中自定義實現(xiàn)了。例如下面的需求:
- 根據(jù)id扣減用戶余額
這看起來是個簡單修改功能,只要修改用戶余額即可。但這個業(yè)務(wù)包含一些業(yè)務(wù)邏輯處理:
- 判斷用戶狀態(tài)是否正常
- 判斷用戶余額是否充足
這些業(yè)務(wù)邏輯都要在service層來做,另外更新余額需要自定義SQL,要在mapper中來實現(xiàn)。因此,我們除了要編寫controller以外,具體的業(yè)務(wù)還要在service和mapper中編寫。
首先在UserController中定義一個方法:
@PutMapping("{id}/deduction/{money}")
@ApiOperation("扣減用戶余額")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
userService.deductBalance(id, money);
}然后是UserService接口:
package com.itheima.mp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.po.User;
public interface IUserService extends IService<User> {
void deductBalance(Long id, Integer money);
}最后是UserServiceImpl實現(xiàn)類:
package com.itheima.mp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.mapper.UserMapper;
import com.itheima.mp.service.IUserService;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Override
public void deductBalance(Long id, Integer money) {
// 1.查詢用戶
User user = getById(id);
// 2.判斷用戶狀態(tài)
if (user == null || user.getStatus() == 2) {
throw new RuntimeException("用戶狀態(tài)異常");
}
// 3.判斷用戶余額
if (user.getBalance() < money) {
throw new RuntimeException("用戶余額不足");
}
// 4.扣減余額
baseMapper.deductMoneyById(id, money);
}
}最后是mapper:
@Update("UPDATE user SET balance = balance - #{money} WHERE id = #{id}")
void deductMoneyById(@Param("id") Long id, @Param("money") Integer money);3.6.2、Lambda
IService中還提供了Lambda功能來簡化我們的復(fù)雜查詢及更新功能。
案例一:實現(xiàn)復(fù)雜條件查詢用戶接口(根據(jù)name,status,minBalance,maxBalance,可以為空)做條件查詢
//實現(xiàn)復(fù)雜條件查詢用戶接口(根據(jù)name,status,minBalance,maxBalance,可以為空)做條件查詢
@ApiOperation("lambdaQuery復(fù)雜條件查詢用戶接口")
@GetMapping("list")
private List<UserVO> listByIds(UserQuery userQuery){
List<User> users = userService.getUserList(userQuery.getName(), userQuery.getStatus(), userQuery.getMinBalance(), userQuery.getMaxBalance());
return BeanUtil.copyToList(users, UserVO.class);
}
@Override
public List<User> getUserList(String name, Integer status, Integer minBalance, Integer maxBalance) {
return lambdaQuery()
.like(name != null, User::getUsername, name)
.eq(status != null, User::getStatus, status)
.ge(minBalance != null, User::getBalance, minBalance)
.le(maxBalance != null, User::getBalance, maxBalance)
.list();
}案例二:根據(jù)id實現(xiàn)扣減余額
//IService的lambdaUpdate方法實現(xiàn)id扣減余額
@ApiOperation("lambdaUpdate方法實現(xiàn)id扣減余額")
@PutMapping("/{id}/lambdaUpdate/{money}")
private void lambdaUpdateById(@ApiParam ("用戶id") @PathVariable Long id,@ApiParam("扣減余額") @PathVariable Long money){
//sql:update user set balance = balance - #{money} where id = #{id};
userService.lambdaUpdateById(id, money);
}
@Override
@Transactional
public void lambdaUpdateById(Long id, Long money) {
//1、查詢用戶
User user=getById(id);
if(user==null||user.getStatus()==2){
throw new RuntimeException("用戶狀態(tài)異常");
}
//2、判斷余額狀態(tài)
if(user.getBalance()-money<0){
throw new RuntimeException("余額不足");
}
long remainBalance=user.getBalance()-money;
lambdaUpdate()
.set(User::getBalance,remainBalance)
.set(remainBalance==0,User::getStatus,2)
.eq(User::getId,id)
.eq(User::getBalance,user.getBalance())//樂觀鎖
.update();
}3.6.3、IService的批量新增
MybatisPlus的批處理是基于PrepareStatement的預(yù)編譯模式,然后批量提交,最終在數(shù)據(jù)庫執(zhí)行時還是會有多條insert語句,逐條插入數(shù)據(jù)。SQL類似這樣:
Preparing: INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) Parameters: user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01 Parameters: user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01 Parameters: user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01
而如果想要得到最佳性能,最好是將多條SQL合并為一條,像這樣:
INSERT INTO user ( username, password, phone, info, balance, create_time, update_time ) VALUES (user_1, 123, 18688190001, "", 2000, 2023-07-01, 2023-07-01), (user_2, 123, 18688190002, "", 2000, 2023-07-01, 2023-07-01), (user_3, 123, 18688190003, "", 2000, 2023-07-01, 2023-07-01), (user_4, 123, 18688190004, "", 2000, 2023-07-01, 2023-07-01);
該怎么做呢?
MySQL的客戶端連接參數(shù)中有這樣的一個參數(shù):rewriteBatchedStatements。顧名思義,就是重寫批處理的statement語句。參考文檔:
這個參數(shù)的默認(rèn)值是false,我們需要修改連接參數(shù),將其配置為true
修改項目中的application.yml文件,在jdbc的url后面添加參數(shù)&rewriteBatchedStatements=true:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: MySQL123再次測試插入數(shù)據(jù),可以發(fā)現(xiàn)速度有非常明顯的提升
3.7、代碼生成
在使用MybatisPlus以后,基礎(chǔ)的Mapper、Service、PO代碼相對固定,重復(fù)編寫也比較麻煩。因此MybatisPlus官方提供了代碼生成器根據(jù)數(shù)據(jù)庫表結(jié)構(gòu)生成PO、Mapper、Service等相關(guān)代碼。只不過代碼生成器同樣要編碼使用,也很麻煩。
這里推薦大家使用一款MybatisPlus的插件,它可以基于圖形化界面完成MybatisPlus的代碼生成,非常簡單。
3.7.1、安裝插件
在Idea的plugins市場中搜索并安裝MyBatisPlus插件:

3.7.2、使用
首先需要配置數(shù)據(jù)庫地址,在Idea頂部菜單中,找到other,選擇Config Database:

在彈出的窗口中填寫數(shù)據(jù)庫連接的基本信息:

點擊OK保存。
然后再次點擊Idea頂部菜單中的other,然后選擇Code Generator:
在彈出的表單中填寫信息:

最終,代碼自動生成到指定的位置了。
3.8、靜態(tài)工具
有的時候Service之間也會相互調(diào)用,為了避免出現(xiàn)循環(huán)依賴問題,MybatisPlus提供一個靜態(tài)工具類Db,其中一些靜態(tài)方法與IService中方法簽名基本一致,也可以幫助我們實現(xiàn)CRUD功能
實例:
@Test
void testDbGet() {
User user = Db.getById(1L, User.class);
System.out.println(user);
}
@Test
void testDbList() {
// 利用Db實現(xiàn)復(fù)雜條件查詢
List<User> list = Db.lambdaQuery(User.class)
.like(User::getUsername, "o")
.ge(User::getBalance, 1000)
.list();
list.forEach(System.out::println);
}
@Test
void testDbUpdate() {
Db.lambdaUpdate(User.class)
.set(User::getBalance, 2000)
.eq(User::getUsername, "Rose");
}3.9、邏輯刪除
對于一些比較重要的數(shù)據(jù),我們往往會采用邏輯刪除的方案,即:
- 在表中添加一個字段標(biāo)記數(shù)據(jù)是否被刪除
- 當(dāng)刪除數(shù)據(jù)時把標(biāo)記置為true
- 查詢時過濾掉標(biāo)記為true的數(shù)據(jù)
一旦采用了邏輯刪除,所有的查詢和刪除邏輯都要跟著變化,非常麻煩。
為了解決這個問題,MybatisPlus就添加了對邏輯刪除的支持。
注意,只有MybatisPlus生成的SQL語句才支持自動的邏輯刪除,自定義SQL需要自己手動處理邏輯刪除。
此時需要給表和實體類額外添加一個邏輯刪除字段
配置邏輯刪除字段
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局邏輯刪除的實體字段名(since 3.3.0,配置后可以忽略不配置步驟2)
logic-delete-value: 1 # 邏輯已刪除值(默認(rèn)為 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認(rèn)為 0)開啟了邏輯刪除功能以后,我們就可以像普通刪除一樣做CRUD,基本不用考慮代碼邏輯問題。還是非常方便的。
注意: 邏輯刪除本身也有自己的問題,比如:
- 會導(dǎo)致數(shù)據(jù)庫表垃圾數(shù)據(jù)越來越多,從而影響查詢效率
- SQL中全都需要對邏輯刪除字段做判斷,影響查詢效率
因此,我不太推薦采用邏輯刪除功能,如果數(shù)據(jù)不能刪除,可以采用把數(shù)據(jù)遷移到其它表的辦法。
3.10、通用枚舉
一般User類中都會有一個Status代表狀態(tài)的屬性,像這種字段我們一般會定義一個枚舉,做業(yè)務(wù)判斷的時候就可以直接基于枚舉做比較。但是我們數(shù)據(jù)庫采用的是int類型,對應(yīng)的PO也是Integer。因此業(yè)務(wù)操作時必須手動把枚舉與Integer轉(zhuǎn)換,非常麻煩。
因此,MybatisPlus提供了一個處理枚舉的類型轉(zhuǎn)換器,可以幫我們把枚舉類型與數(shù)據(jù)庫類型自動轉(zhuǎn)換
定義枚舉類:
package com.itheima.mp.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;
@Getter
public enum UserStatus {
NORMAL(1, "正常"),
FREEZE(2, "凍結(jié)")
;
@EnumValue
private final int value;
private final String desc;
UserStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
}要讓MybatisPlus處理枚舉與數(shù)據(jù)庫類型自動轉(zhuǎn)換,我們必須告訴MybatisPlus,枚舉中的哪個字段的值作為數(shù)據(jù)庫值。 MybatisPlus提供了@EnumValue注解來標(biāo)記枚舉屬性:
配置枚舉處理類
mybatis-plus:
configuration:
default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler3.11、JSON類型處理器
數(shù)據(jù)庫的user表中有一個info字段,是JSON類型

格式為:{"age": 20, "intro": "佛系青年", "gender": "male"}
而一般類中都是用String類型去存儲的,
這樣一來,我們要讀取info中的屬性時就非常不方便。如果要方便獲取,info的類型最好是一個Map或者實體類。
而一旦我們把info改為對象類型,就需要在寫入數(shù)據(jù)庫時手動轉(zhuǎn)為String,再讀取數(shù)據(jù)庫時,手動轉(zhuǎn)換為對象,這會非常麻煩。
因此MybatisPlus提供了很多特殊類型字段的類型處理器,解決特殊字段類型與數(shù)據(jù)庫類型轉(zhuǎn)換的問題。例如處理JSON就可以使用JacksonTypeHandler處理器。
接下來,我們就來看看這個處理器該如何使用。
定義一個實體:
package com.itheima.mp.domain.po;
import lombok.Data;
@Data
public class UserInfo {
private Integer age;
private String intro;
private String gender;
}接下來,將User類的info字段修改為UserInfo類型,并聲明類型處理器:

四、插件功能
MybatisPlus提供了很多的插件功能,進(jìn)一步拓展其功能。目前已有的插件有:
PaginationInnerInterceptor:自動分頁TenantLineInnerInterceptor:多租戶DynamicTableNameInnerInterceptor:動態(tài)表名OptimisticLockerInnerInterceptor:樂觀鎖IllegalSQLInnerInterceptor:sql 性能規(guī)范BlockAttackInnerInterceptor:防止全表更新與刪除
4.1、分頁插件
使用多個分頁插件的時候需要注意插件定義順序,建議使用順序如下:
- 多租戶,動態(tài)表名
- 分頁,樂觀鎖
- sql 性能規(guī)范,防止全表更新與刪除
在未引入分頁插件的情況下,MybatisPlus是不支持分頁功能的,IService和BaseMapper中的分頁方法都無法正常起效。 所以,我們必須配置分頁插件。
配置類:
package com.itheima.mp.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;
@Configuration
public class MybatisConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
// 初始化核心插件
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分頁插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}測試:
@Test
void testPageQuery() {
// 1.分頁查詢,new Page()的兩個參數(shù)分別是:頁碼、每頁大小
Page<User> p = userService.page(new Page<>(2, 2));
// 2.總條數(shù)
System.out.println("total = " + p.getTotal());
// 3.總頁數(shù)
System.out.println("pages = " + p.getPages());
// 4.數(shù)據(jù)
List<User> records = p.getRecords();
records.forEach(System.out::println);
}到此這篇關(guān)于Mybatis-Plus超級實用的多種功能用法的文章就介紹到這了,更多相關(guān)MybatisPlus用法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JVM---jstack分析Java線程CPU占用,線程死鎖的解決
這篇文章主要介紹了JVM---jstack分析Java線程CPU占用,線程死鎖的解決,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
利用Java+Selenium+OpenCV模擬實現(xiàn)網(wǎng)頁滑動驗證
目前很多網(wǎng)頁都有滑動驗證,目的就是防止不良爬蟲扒他們網(wǎng)站的數(shù)據(jù)。本文將介紹通過Java Selenium OpenCV解決網(wǎng)頁滑塊驗證,需要的可以參考一下2022-01-01
從try-with-resources到ThreadLocal,優(yōu)化你的代碼編寫方式
這篇文章主要為大家介紹了從try-with-resources到ThreadLocal,優(yōu)化代碼的編寫方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
SpringBoot啟動失敗的解決方法:A component required a&nb
這篇文章主要介紹了解決SpringBoot啟動失?。篈 component required a bean of type ‘xxxxxxx‘ that could not be found.,目前解決方法有兩種,一種是不注入bean的方式,另一種是使用@Component的方式,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-02-02
詳解Java8新特性Stream之list轉(zhuǎn)map及問題解決
這篇文章主要介紹了詳解Java8新特性Stream之list轉(zhuǎn)map及問題解決,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09

