免費(fèi)b站推廣網(wǎng)站app如何讓百度搜索排名靠前
2.核心功能
剛才的案例中都是以id
為條件的簡單CRUD
,一些復(fù)雜條件的SQL
語句就要用到一些更高級的功能了。
2.1.條件構(gòu)造器
除了新增以外,修改、刪除、查詢的SQL
語句都需要指定where
條件。因此BaseMapper
中提供的相關(guān)方法除了以id
作為where
條件以外,還支持更加復(fù)雜的where
條件。
參數(shù)中的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.1.QueryWrapper
無論是修改、刪除、查詢,都可以使用QueryWrapper
來構(gòu)建查詢條件。接下來看一些例子:
1.查詢:查詢出名字中帶o
的,存款大于等于1000
元的人。代碼如下:
@Test
void testQueryWrapper() {// 1.構(gòu)建查詢條件 where name like "%o%" AND balance >= 1000QueryWrapper<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字段都會(huì)作為set語句User user = new User();user.setBalance(2000);userMapper.update(user, wrapper);
}
2.1.2.UpdateWrapper
基于BaseMapper
中的update
方法更新時(shí)只能直接賦值,對于一些復(fù)雜的需求就難以實(shí)現(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)有值的,這個(gè)時(shí)候就要利用UpdateWrapper
中的setSql
功能了:
// 更新id為1,2,4的用戶的余額,扣200@Testvoid testUpdateWrapper() {// List<Long> ids = new ArrayList<>(Arrays.asList(1L, 2L, 4L)); //JDK8 可以使用這種方式List<Long> ids = List.of(1L, 2L, 4L); // JDK9 之后才有的of方法// 1.生成SQLUpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<User>().setSql("balance = balance -200").in("id", ids); // WHERE id in (1, 2, 4)// 2.更新,注意第一個(gè)參數(shù)可以給null,也就是不填更新字段和數(shù)據(jù),// 而是基于UpdateWrapper中的setSql來更新userMapper.update(null, userUpdateWrapper);}
2.1.3.LambdaQueryWrapper
無論是QueryWrapper
還是UpdateWrapper
在構(gòu)造條件的時(shí)候都需要寫死字段名稱,會(huì)出現(xiàn)字符串魔法值
。這在編程規(guī)范中顯然是不推薦的。
那怎么樣才能不寫字段名,又能知道字段名呢?
其中一種辦法是基于變量的gettter
方法結(jié)合反射技術(shù)。因此我們只要將條件對應(yīng)的字段的getter
方法傳遞給MybatisPlus
,它就能計(jì)算出對應(yīng)的變量名了。而傳遞方法可以使用JDK8
中的方法引用和Lambda
表達(dá)式。
因此MybatisPlus
又提供了一套基于Lambda的Wrapper
,包含兩個(gè):
-LambdaQueryWrapper
LambdaUpdateWrapper
分別對應(yīng)QueryWrapper
和UpdateWrapper
其使用方式如下:
@Testvoid testLambdaQueryWrapper() {// 1.構(gòu)建查詢條件 where name like "%o%" AND balance >= 1000LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<User>().select(User::getId, User::getUsername, User::getInfo, User::getBalance).like(User::getUsername, "o").ge(User::getBalance, 1000);// 2.查詢數(shù)據(jù)List<User> users = userMapper.selectList(userLambdaQueryWrapper);users.forEach(System.out::println);}// 更新用戶名為jack的用戶的余額為2000@Testvoid testLambdaUpdateByQueryWrapper() {// 1.構(gòu)建查詢條件 where name = "Jack"LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<User>().eq(User::getUsername, "Jack");// 2.更新數(shù)據(jù),user中非null字段都會(huì)作為set語句User user = new User();user.setBalance(2000);userMapper.update(user, wrapper);}
2.2.自定義SQL
在演示UpdateWrapper
的案例中,我們在代碼中編寫了更新的SQL
語句:
這種寫法在某些企業(yè)也是不允許的,因?yàn)?code>SQL語句最好都維護(hù)在持久層,而不是業(yè)務(wù)層。就當(dāng)前案例來說,由于條件是in
語句,只能將SQL
寫在Mapper.xml
文件,利用foreach
來生成動(dòng)態(tài)SQL
。
這實(shí)在是太麻煩了。假如查詢條件更復(fù)雜,動(dòng)態(tài)SQL
的編寫也會(huì)更加復(fù)雜。
所以,MybatisPlus
提供了自定義SQL
功能,可以讓我們利用Wrapper
生成查詢條件,再結(jié)合Mapper.xml
編寫SQL
2.2.1.基本用法
以當(dāng)前案例來說,我們可以這樣寫:
@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的自定義方法,直接傳遞WrapperuserMapper.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);
}
這樣就省去了編寫復(fù)雜查詢條件的煩惱了。
總結(jié)
在
mapper
方法參數(shù)中用Param
注解聲明wrapper
變量名稱,必須是ew
。
為什么必須要用ew
?mybatisPlus
源碼規(guī)定的,這也成為一種約定俗成的規(guī)范。當(dāng)然,你也可以手動(dòng)修改生成的代碼,將ew
改為其他名字,但這樣可能導(dǎo)致一些示例代碼不適用。
2.2.2.多表關(guān)聯(lián)
理論上來講MyBatisPlus
是不支持多表查詢的,不過我們可以利用Wrapper
中自定義條件結(jié)合自定義SQL
來實(shí)現(xiàn)多表查詢的效果。
例如,我們要查詢出所有收貨地址在北京的并且用戶id
在1、2、4
之中的用戶
要是自己基于mybatis
實(shí)現(xiàn)SQL
,大概是這樣的:
<select id="queryUserByIdAndAddr" resultType="com.itheima.mp.domain.po.User">SELECT *FROM user uINNER JOIN address a ON u.id = a.user_idWHERE u.id<foreach collection="ids" separator="," item="id" open="IN (" close=")">#{id}</foreach>AND a.city = #{city}</select>
可以看出其中最復(fù)雜的就是WHERE
條件的編寫,如果業(yè)務(wù)復(fù)雜一些,這里的SQL
會(huì)更變態(tài)。
但是基于自定義SQL
結(jié)合Wrapper
的玩法,我們就可以利用Wrapper
來構(gòu)建查詢條件,然后手寫SELECT
及FROM
部分,實(shí)現(xiàn)多表查詢。
查詢條件這樣來構(gòu)建:
@Test
void testCustomJoinWrapper() {// 1.準(zhǔn)備自定義查詢條件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("u.id", List.of(1L, 2L, 4L)).eq("a.city", "北京");// 2.調(diào)用mapper的自定義方法List<User> users = userMapper.queryUserByWrapper(wrapper);users.forEach(System.out::println);
}
然后在UserMapper
中自定義方法:
@Select("SELECT u.* FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}")
List<User> queryUserByWrapper(@Param("ew")QueryWrapper<User> wrapper);
當(dāng)然,也可以在UserMapper.xml
中寫SQL
:
<select id="queryUserByWrapper" resultType="com.itheima.mp.domain.po.User">SELECT * FROM user u INNER JOIN address a ON u.id = a.user_id ${ew.customSqlSegment}
</select>
2.3.Service接口
MybatisPlus
不僅提供了BaseMapper
,還提供了通用的Service
接口及默認(rèn)實(shí)現(xiàn),封裝了一些常用的service
模板方法。
通用接口為IService
,默認(rèn)實(shí)現(xiàn)為ServiceImpl
,其中封裝的方法可以分為以下幾類:
save
:新增remove
:刪除update
:更新get
:查詢單個(gè)結(jié)果list
:查詢集合結(jié)果count
:計(jì)數(shù)page
:分頁查詢
2.3.1.CRUD
我們先倆看下基本的CRUD
接口。
新增:
save
是新增單個(gè)元素saveBatch
是批量新增saveOrUpdate
是根據(jù)id
判斷,如果數(shù)據(jù)存在就更新,不存在則新增saveOrUpdateBatch
是批量的新增或修改
刪除:
removeById
:根據(jù)id
刪除removeByIds
:根據(jù)id
批量刪除removeByMap
:根據(jù)Map
中的鍵值對為條件刪除remove(Wrapper<T>)
:根據(jù)Wrapper
條件刪除:暫不支持removeBatchByIds
修改:
updateById
:根據(jù)id
修改update(Wrapper<T>)
:根據(jù)UpdateWrapper
修改,Wrapper
中包含set
和where
部分update(T,Wrapper<T>)
:按照T內(nèi)的數(shù)據(jù)修改與Wrapper
匹配到的數(shù)據(jù)updateBatchById
:根據(jù)id
批量修改
查詢
Get
:
getById
:根據(jù)id
查詢1
條數(shù)據(jù)getOne(Wrapper<T>)
:根據(jù)Wrapper
查詢1
條數(shù)據(jù)getBaseMapper
:獲取Service
內(nèi)的BaseMapper
實(shí)現(xiàn),某些時(shí)候需要直接調(diào)用Mapper
內(nèi)的自定義SQL
時(shí)可以用這個(gè)方法獲取到Mapper
List
:
listByIds
:根據(jù)id
批量查詢list(Wrapper<T>)
:根據(jù)Wrapper
條件查詢多條數(shù)據(jù)list()
:查詢所有
Count
:
count()
:統(tǒng)計(jì)所有數(shù)量count(Wrapper<T>)
:統(tǒng)計(jì)符合Wrapper
條件的數(shù)據(jù)數(shù)量
getBaseMapper
:
當(dāng)我們在service
中要調(diào)用Mapper
中自定義SQL
時(shí),就必須獲取service
對應(yīng)的Mapper
,就可以通過這個(gè)方法:
項(xiàng)目結(jié)構(gòu)如下:
來到 UserService
中創(chuàng)建 測試類
接下來,我們快速實(shí)現(xiàn)下面4個(gè)接口:
首先,我們在項(xiàng)目中引入幾個(gè)依賴:
pom.xml
<!--swagger-->
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi2-spring-boot-starter</artifactId><version>4.1.0</version>
</dependency>
<!--web-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后在application.xml
需要配置swagger
信息:
knife4j:enable: trueopenapi:title: 用戶管理接口文檔description: "用戶管理接口文檔"email: zhanghuyi@itcast.cnconcat: 墨苒孤url: https://www.itcast.cnversion: v1.0.0group:default:group-name: defaultapi-rule: packageapi-rule-resources:- com.itheima.mp.controller
Alt + 8
選擇 springboot
服務(wù),啟動(dòng),瀏覽器輸入 http://localhost:8080/doc.html#/home
然后,接口需要兩個(gè)實(shí)體:
UserFormDTO
:代表新增時(shí)的用戶表單UserVO
:代表查詢的返回結(jié)果
首先是UserFormDTO
:
package com.itheima.mp.domain.dto;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用戶表單實(shí)體")
public class UserFormDTO {@ApiModelProperty("id")private Long id;@ApiModelProperty("用戶名")private String username;@ApiModelProperty("密碼")private String password;@ApiModelProperty("注冊手機(jī)號")private String phone;@ApiModelProperty("詳細(xì)信息,JSON風(fēng)格")private String info;@ApiModelProperty("賬戶余額")private Integer balance;
}
然后是UserVO
:
package com.itheima.mp.domain.vo;import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;@Data
@ApiModel(description = "用戶VO實(shí)體")
public class UserVO {@ApiModelProperty("用戶id")private Long id;@ApiModelProperty("用戶名")private String username;@ApiModelProperty("詳細(xì)信息")private String info;@ApiModelProperty("使用狀態(tài)(1正常 2凍結(jié))")private Integer status;@ApiModelProperty("賬戶余額")private Integer balance;
}
用 @Autowired
注入,Spring
并不推薦,而是推薦我們使用構(gòu)造函數(shù)注入
構(gòu)造函數(shù)注入如下:
但是這樣會(huì)帶來一個(gè)問題,如果成員很多,構(gòu)造函數(shù)就會(huì)很多,看起來很繁瑣。那么就可以使用Lombok
注解幫我們簡化
上圖這樣看起來是可以了,但是,一個(gè)類可以有很多的成員,并不是每一個(gè)都是需要注入的。那么怎么區(qū)分哪些需要注入,哪些不需要注入呢?
可以給 成員 加上 final
來區(qū)分, 這樣就必須在類初始化的時(shí)候,對加了 final
的成員進(jìn)行初始化,所以最終寫法如下:
@RequiredArgsConstructor
表示 會(huì)對 final
修飾的成員進(jìn)行創(chuàng)建構(gòu)造函數(shù),沒有修飾就不會(huì)生成構(gòu)造函數(shù)
新增接口實(shí)現(xiàn):
@PostMapping@ApiOperation("新增用戶")public void saveUser(@RequestBody UserFormDTO userFormDTO) {// 1.轉(zhuǎn)換DTO為POUser user = BeanUtil.copyProperties(userFormDTO, User.class);// 2.新增userService.save(user);}
由于傳入的 是 DTO
, 而我們需要保存的是 PO
,所以需要先把DTO
轉(zhuǎn)換為PO
。這里使用胡圖工具包,其余接口同理
@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.處理po轉(zhuǎn)voreturn 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.處理po轉(zhuǎn)vo (集合用copyToList)return BeanUtil.copyToList(users, UserVO.class);}
代碼在github
https://github.com/RanGuMo/mp-demo