前言
MyBatisPlus詳解系列文章:
MyBatisPlus詳解(一)項(xiàng)目搭建、@TableName、@TableId、@TableField注解與常見(jiàn)配置
2 核心功能
2.1 條件構(gòu)造器
2.1.1 Wrapper
在BaseMapper接口提供的相關(guān)方法中,除了以id作為where條件,還支持更加復(fù)雜的where條件,即條件構(gòu)造器Wrapper:
Wrapper是條件構(gòu)造器的抽象類,其下有很多默認(rèn)實(shí)現(xiàn),繼承關(guān)系如圖:
Wrapper的子類AbstractWrapper提供了where中包含的所有條件構(gòu)造方法:
而QueryWrapper在AbstractWrapper的基礎(chǔ)上拓展了一個(gè)select
方法,允許指定查詢字段:
而UpdateWrapper在AbstractWrapper的基礎(chǔ)上拓展了一個(gè)set
方法,允許指定SQL中的SET部分:
2.1.2 QueryWrapper
使用QueryWrapper構(gòu)建查詢條件,例如:
select id, username, info, balance from t_user where id = 3 and username like '%o%' and balance >= 1000
@Test
public void testQueryWrapper() {
// select id, username, info, balance from t_user
// where id = 3 and username like '%o%' and balance >= 1000
QueryWrapper<User> wrapper = new QueryWrapper<User>()
// 設(shè)置需要查詢的字段
.select("id", "username", "info", "balance")
// id = 3
.eq("id", 3)
// like '%o%'
.like("username", "o")
// balance >= 1000
.ge("balance", 1000);
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
執(zhí)行以上單元測(cè)試結(jié)果如下:
==> Preparing: SELECT id,username,info,balance FROM t_user WHERE (id = ? AND username LIKE ? AND balance >= ?)
==> Parameters: 3(Integer), %o%(String), 1000(Integer)
<== Total: 1
User(id=3, username=Hope, password=null, phone=null, info={"age": 25, "intro": "上進(jìn)青年", "gender": "male"}, status=null, balance=20000, createTime=null, updateTime=null)
update t_user set balance = 2000 where username = 'Jack'
@Test
void testUpdateByQueryWrapper() {
// update t_user set balance = 2000 where username = 'Jack'
QueryWrapper<User> wrapper = new QueryWrapper<User>()
// username = 'Jack'
.eq("username", "Jack");
// 2.更新數(shù)據(jù)
User user = new User();
user.setBalance(2000);
userMapper.update(user, wrapper);
}
執(zhí)行以上單元測(cè)試結(jié)果如下:
==> Preparing: UPDATE t_user SET balance=? WHERE (username = ?)
==> Parameters: 2000(Integer), Jack(String)
<== Updates: 0
2.1.3 UpdateWrapper
如上面的單元測(cè)試testUpdateByQueryWrapper()
所示,update()
方法更新時(shí)只能直接賦值,對(duì)于一些復(fù)雜的需求就難以實(shí)現(xiàn),例如:
UPDATE t_user SET balance = balance - 200 WHERE id in (1, 2, 4);
SET語(yǔ)句的賦值結(jié)果是基于字段現(xiàn)有值的,這個(gè)時(shí)候就要利用UpdateWrapper中的setSql()
方法了:
@Test
void testUpdateWrapper() {
List<Long> ids = Arrays.asList(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
// SET balance = balance - 200
.setSql("balance = balance - 200")
// WHERE id in (1, 2, 4)
.in("id", ids);
// 2.更新
// 注意第一個(gè)參數(shù)為null,則會(huì)基于UpdateWrapper中的setSQL來(lái)更新
userMapper.update(null, wrapper);
}
執(zhí)行以上單元測(cè)試結(jié)果如下:
==> Preparing: UPDATE t_user SET balance = balance - 200 WHERE (id IN (?,?,?))
==> Parameters: 1(Long), 2(Long), 4(Long)
<== Updates: 2
2.1.4 LambdaQueryWrapper
無(wú)論是QueryWrapper還是UpdateWrapper,在構(gòu)造條件的時(shí)候都需要寫死字段名稱,會(huì)出現(xiàn)字符串魔法值。這在編程規(guī)范中顯然是不推薦的。
而要不寫字段名,又能知道字段名,其中一種辦法是基于變量的gettter
方法結(jié)合反射技術(shù)。為此,MybatisPlus又提供了一套基于Lambda的Wrapper,包含兩個(gè):LambdaQueryWrapper和LambdaUpdateWrapper。
@Test
public void testLambdaQueryWrapper() {
// select id, username, info, balance from t_user
// where id = 3 and username like '%o%' and balance >= 1000
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>()
// 設(shè)置需要查詢的字段
.select(User::getId, User::getUsername, User::getInfo, User::getBalance)
// id = 3
.eq(User::getId, 3)
// username like '%o%'
.like(User::getUsername, "o")
// balance >= 1000
.ge(User::getBalance, 1000);
List<User> userList = userMapper.selectList(wrapper);
userList.forEach(System.out::println);
}
執(zhí)行該單元測(cè)試,可以得到和上面的testQueryWrapper()
一樣的結(jié)果,但這種寫法不需要將字段名寫死。未來(lái)如果需要修改字段名,也不需要這里的邏輯代碼。
2.2 自定義SQL
在單元測(cè)試testUpdateWrapper()
,編寫了這樣一行代碼:.setSql("balance = balance - 200")
,這種寫法其實(shí)也是不好的,因?yàn)?strong>SQL語(yǔ)句最好都維護(hù)在持久層,而不是業(yè)務(wù)層。
為此,MyBatis提供了自定義SQL功能,即利用Wrapper構(gòu)建復(fù)雜的where條件,然后自己定義SQL語(yǔ)句剩余的部分。
也就是說(shuō),UPDATE t_user SET balance = balance - 200
自己定義,WHERE id in (1, 2, 4)
利用Wrapper構(gòu)建。
2.2.1 基本用法
例如單元測(cè)試testUpdateWrapper()
可以這樣修改:
@Test
public void testUpdateWrapper2() {
List<Long> ids = Arrays.asList(1L, 2L, 4L);
// 1.生成SQL
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
// SET balance = balance - 200
// .setSql("balance = balance - 200")
// WHERE id in (1, 2, 4)
.in("id", ids);
// 2.更新
// userMapper.update(null, wrapper);
// 改為調(diào)用自定義的Mapper方法,直接傳遞Wrapper
userMapper.deductBalanceByIds(200, wrapper);
}
然后在UserMapper中定義deductBalanceByIds()
方法:
// com.star.learning.mapper.UserMapper
@Update("UPDATE t_user SET balance = balance - #{money} ${uw.customSqlSegment}")
void deductBalanceByIds(@Param("money") int money, @Param("uw") UpdateWrapper<User> wrapper);
執(zhí)行修改后的單元測(cè)試,執(zhí)行結(jié)果和原來(lái)的是一樣的。
2.2.2 多表關(guān)聯(lián)
理論上來(lái)講MyBatisPlus是不支持多表查詢的,不過(guò)仍然可以利用Wrapper構(gòu)建復(fù)雜where條件結(jié)合自定義SQL來(lái)實(shí)現(xiàn)多表查詢的效果。
例如,要查詢出所有收貨地址在北京并且用戶id在1、2、4之中的用戶,其SQL語(yǔ)句為:
SELECT * FROM t_user u
INNER JOIN t_address a ON a.user_id = u.id
WHERE u.id IN (1,2,4)
AND a.city = '北京'
編寫單元測(cè)試,利用Wrapper構(gòu)建復(fù)雜where條件,并調(diào)用自定義的Mapper方法:
@Test
public void testMultiTableQuery() {
List<Long> ids = Arrays.asList(1L, 2L, 4L);
// 1.利用Wrapper構(gòu)建復(fù)雜where條件
QueryWrapper<User> wrapper = new QueryWrapper<User>()
.in("u.id", ids)
.eq("a.city", "北京");
// 2.調(diào)用自定義的Mapper方法
User user = userMapper.queryUserByIdAndCity(wrapper);
System.out.println(user);
}
// com.star.learning.mapper.UserMapper
@Select("SELECT * FROM t_user u INNER JOIN t_address a ON a.user_id = u.id ${ew.customSqlSegment}")
User queryUserByIdAndCity(@Param("ew") QueryWrapper<User> wrapper);
執(zhí)行以上單元測(cè)試,結(jié)果如下:
==> Preparing: SELECT * FROM t_user u INNER JOIN t_address a ON a.user_id = u.id WHERE (u.id IN (?,?,?) AND a.city = ?)
==> Parameters: 1(Long), 2(Long), 4(Long), 北京(String)
<== Total: 1
User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青澀少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20)
2.3 Service接口
MybatisPlus不僅提供了BaseMapper接口,還提供了通用的Service接口及默認(rèn)實(shí)現(xiàn)ServiceImpl,封裝了一些常用的service模板方法。
2.3.1 IService
2.3.1.1 save
-
save
:插入一條記錄 -
saveBatch
:批量插入多條記錄 -
saveOrUpdate
:記錄存在則更新記錄,否則插入一條新記錄 -
saveOrUpdateBatch
:記錄存在則批量修改,否則批量插入
2.3.1.2 remove
-
remove
:根據(jù)條件刪除 -
removeById
:根據(jù)ID刪除 -
removeByIds
:根據(jù)ID批量刪除 -
removeByMap
:根據(jù)Map中的鍵值對(duì)為條件刪除 -
removeBatchByIds
:批量刪除(jdbc批量提交)
2.3.1.3 update
-
update(Wrapper<T>)
:根據(jù)UpdateWrapper修改,Wrapper中包含set和where部分 -
update(T,Wrapper<T>)
:按照T內(nèi)的數(shù)據(jù)修改與Wrapper匹配到的數(shù)據(jù) -
updateById
:根據(jù)id修改 -
updateBatchById
:根據(jù)id批量修改
2.3.1.4 get
-
getById
:根據(jù)id查詢 -
getOne(Wrapper<T>)
:根據(jù)Wrapper查詢1條記錄 -
getBaseMapper
:獲取對(duì)應(yīng)實(shí)體的BaseMapper -
getMap(Wrapper<T>)
:根據(jù)Wrapper查詢1條記錄,封裝為Map
2.3.1.5 list
-
list()
:查詢所有 -
list(Wrapper<T>)
:根據(jù)Wrapper條件查詢列表 -
listByIds()
:根據(jù)ID集合查詢列表
2.3.1.6 count
-
count()
:查詢總記錄數(shù) -
count(Wrapper<T>)
:根據(jù)Wrapper條件查詢總記錄數(shù)
2.3.1.7 page
-
page(IPage)
:無(wú)條件翻頁(yè)查詢 -
page(IPage, Wrapper<T>)
:根據(jù)Wrapper條件翻頁(yè)查詢
2.3.2 基本用法
創(chuàng)建一個(gè)IUserService接口,繼承IService接口以拓展方法;同時(shí),自定義UserServiceImpl實(shí)現(xiàn)類,繼承ServiceImpl實(shí)現(xiàn)類,并實(shí)現(xiàn)IUserService接口。
// com.star.learning.service.IUserService
public interface IUserService extends IService<User> {
}
// com.star.learning.service.impl.UserServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
這樣配置之后,導(dǎo)入IUserService接口,即可以使用IService接口中定義的各種方法。
接下來(lái),實(shí)現(xiàn)下面這個(gè)接口:
接口 | 請(qǐng)求方式 | 請(qǐng)求路徑 | 請(qǐng)求參數(shù) | 返回值 |
---|---|---|---|---|
新增用戶 | POST | /user/add | User | 無(wú) |
首先,導(dǎo)入相關(guān)依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后創(chuàng)建一個(gè)UserController類,并編寫一個(gè)addUser()
方法:
// com.star.learning.controller.UserController
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private IUserService userService;
@PostMapping("/add")
public void addUser(@RequestBody User user) {
boolean save = userService.save(user);
System.out.println("新增用戶結(jié)果 => " + save);
}
}
最后進(jìn)行功能測(cè)試,調(diào)用/user/add
接口,查看控制臺(tái)打印信息:
==> Preparing: INSERT INTO t_user ( username, password, phone, info, balance ) VALUES ( ?, ?, ?, ?, ? )
==> Parameters: Jim(String), 123456(String), 999(String), {"age": 20, "intro": "佛系青年", "gender": "male"}(String), 500(Integer)
<== Updates: 1
新增用戶結(jié)果 => true
接下來(lái)再來(lái)實(shí)現(xiàn)3個(gè)接口:
接口 | 請(qǐng)求方式 | 請(qǐng)求路徑 | 請(qǐng)求參數(shù) | 返回值 |
---|---|---|---|---|
根據(jù)id查詢用戶 | GET | /user/{id} | id | User |
根據(jù)id集合批量查詢用戶 | GET | /user/{id} | id | List<User> |
刪除用戶 | DELETE | /user/{id} | id | 無(wú) |
其代碼如下:
// com.star.learning.controller.UserController
@GetMapping("/{id}")
public User getById(@PathVariable("id") Long userId) {
User user = userService.getById(userId);
System.out.println("根據(jù)id查詢用戶 => " + user);
return user;
}
@GetMapping
public List<User> queryUserByIds(@RequestParam("ids") List<Long> ids){
List<User> users = userService.listByIds(ids);
System.out.println("根據(jù)id集合查詢用戶列表 => " + users);
return users;
}
@DeleteMapping("/{id}")
public void removeUserById(@PathVariable("id") Long userId){
boolean remove = userService.removeById(userId);
System.out.println("根據(jù)id刪除用戶 => " + remove);
}
調(diào)用這3個(gè)接口,查看控制臺(tái)打印信息:
// 根據(jù)id查詢用戶
==> Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
==> Parameters: 2(Long)
<== Total: 1
根據(jù)id查詢用戶 => User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青澀少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20)
// 根據(jù)id集合批量查詢用戶
==> Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id IN ( ? , ? , ? )
==> Parameters: 1(Long), 2(Long), 3(Long)
<== Total: 2
根據(jù)id集合查詢用戶列表 => [User(id=2, username=Rose, password=123, phone=13900112223, info={"age": 19, "intro": "青澀少女", "gender": "female"}, status=1, balance=200, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T16:10:20), User(id=3, username=Hope, password=123, phone=13900112222, info={"age": 25, "intro": "上進(jìn)青年", "gender": "male"}, status=1, balance=20000, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T11:12:48)]
// 根據(jù)id刪除用戶
==> Preparing: DELETE FROM t_user WHERE id=?
==> Parameters: 2(Long)
<== Updates: 1
根據(jù)id刪除用戶 => true
可以看到,上述4個(gè)接口都直接在Controller類中即可實(shí)現(xiàn),無(wú)需編寫任何Service代碼,非常方便。
不過(guò),一些帶有業(yè)務(wù)邏輯的接口則需要在Service中自定義實(shí)現(xiàn)了。例如下面這個(gè)接口:
接口 | 請(qǐng)求方式 | 請(qǐng)求路徑 | 請(qǐng)求參數(shù) | 返回值 |
---|---|---|---|---|
根據(jù)id扣減用戶余額 | PUT | {id}/deduction/{money} | id,money | 無(wú) |
// com.star.learning.controller.UserController
@PutMapping("{id}/deduction/{money}")
public void deductBalance(@PathVariable("id") Long id, @PathVariable("money")Integer money){
System.out.println("扣減id=" + id + "的用戶的余額" + money);
userService.deductBalance(id, money);
}
// com.star.learning.service.IUserService
public interface IUserService extends IService<User> {
void deductBalance(Long userId, Integer money);
}
// com.star.learning.service.impl.UserServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public void deductBalance(Long userId, Integer money) {
// 1.查詢用戶
User user = getById(userId);
System.out.println(user);
// 2.判斷用戶狀態(tài)
if (user == null || user.getStatus() == 2) {
throw new RuntimeException("用戶狀態(tài)異常");
}
// 3.判斷用戶余額
if (user.getBalance() < money) {
throw new RuntimeException("用戶余額不足");
}
// 4.扣減余額
// 2.2節(jié)自定義SQL創(chuàng)建了deductBalanceByIds方法可以直接使用
UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
.eq("id", userId);
userMapper.deductBalanceByIds(money, wrapper);
}
}
調(diào)用/user/3/deduction/200
接口,查看控制臺(tái)打印信息:
扣減id=3的用戶的余額200
==> Preparing: SELECT id,username,password,phone,info,status,balance,create_time,update_time FROM t_user WHERE id=?
==> Parameters: 3(Long)
<== Total: 1
User(id=3, username=Hope, password=123, phone=13900112222, info={"age": 25, "intro": "上進(jìn)青年", "gender": "male"}, status=1, balance=20000, createTime=2024-04-21T10:13:35, updateTime=2024-04-21T11:12:48)
==> Preparing: UPDATE t_user SET balance = balance - ? WHERE (id = ?)
==> Parameters: 200(Integer), 3(Long)
<== Updates: 1
…
本節(jié)完,更多內(nèi)容請(qǐng)查閱分類專欄:MyBatisPlus詳解
本文涉及代碼下載地址:https://gitee.com/weidag/mybatis_plus_learning.git文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-857943.html
感興趣的讀者還可以查閱我的另外幾個(gè)專欄:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-857943.html
- SpringBoot源碼解讀與原理分析(已完結(jié))
- MyBatis3源碼深度解析(已完結(jié))
- Redis從入門到精通(已完結(jié))
- 再探Java為面試賦能(持續(xù)更新中…)
到了這里,關(guān)于MyBatisPlus詳解(二)條件構(gòu)造器Wrapper、自定義SQL、Service接口的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!