做算法題的 網(wǎng)站成品網(wǎng)站貨源1
微服務(wù)-MybatisPlus下
文章目錄
- 微服務(wù)-MybatisPlus下
- 1 MybatisPlus擴(kuò)展功能
- 1.1 代碼生成
- 1.2 靜態(tài)工具
- 1.3 邏輯刪除
- 1.4 枚舉處理器
- 1.5 JSON處理器
- **1.5.1.定義實(shí)體**
- **1.5.2.使用類(lèi)型處理器**
- **1.6 配置加密(選學(xué))**
- 1.6.1.生成秘鑰
- **1.6.2.修改配置**
- **1.6.3.測(cè)試**
- 2 插件功能
- 2.1 分頁(yè)插件
- 2.2 通用分頁(yè)實(shí)體
- 2.2.1 簡(jiǎn)單分頁(yè)查詢(xún)
- 2.2.2 通用分頁(yè)查詢(xún)
1 MybatisPlus擴(kuò)展功能
1.1 代碼生成
自己從頭開(kāi)始,則步驟如下圖所示
1.2 靜態(tài)工具
有的時(shí)候Service之間也會(huì)相互調(diào)用,為了避免出現(xiàn)循環(huán)依賴(lài)問(wèn)題,MybatisPlus提供一個(gè)靜態(tài)工具類(lèi):Db
,其中的一些靜態(tài)方法與IService
中方法 簽名基本一致。主要差別就是由于是靜態(tài)的,之前沒(méi)有指定過(guò)實(shí)體對(duì)象,所以除save、update以外的函數(shù)幾乎都要傳入實(shí)體類(lèi),讓底層調(diào)用反射機(jī)制,得到實(shí)體類(lèi)。而save、update由于本身參數(shù)就需要傳入一個(gè)實(shí)體類(lèi)對(duì)象,此對(duì)象就可通過(guò)反射得到實(shí)體類(lèi),所以不需要再傳入實(shí)體類(lèi)。
實(shí)現(xiàn)CRUD功能:
案例:靜態(tài)工具查詢(xún)
需求:
- 改造根據(jù)id查詢(xún)用戶(hù)的接口,查詢(xún)用戶(hù)的同時(shí),查詢(xún)出用戶(hù)對(duì)應(yīng)的所有地址
- 改造根據(jù)id批量查詢(xún)用戶(hù)的接口,查詢(xún)用戶(hù)的同時(shí),查詢(xún)出用戶(hù)對(duì)應(yīng)的所有地址
- 實(shí)現(xiàn)根據(jù)用戶(hù)id查詢(xún)收貨地址功能,需要驗(yàn)證用戶(hù)狀態(tài),凍結(jié)用戶(hù)拋出異常(練習(xí))
可以看到以上需求一般都需要在注入了userService之后,再注入addressService才能實(shí)現(xiàn)(當(dāng)然你用多表聯(lián)合或者注入mapper當(dāng)我沒(méi)說(shuō))。那這就涉及到了多個(gè)Service的相互調(diào)用,出現(xiàn)循環(huán)依賴(lài)問(wèn)題,這種情況下就可以使用靜態(tài)工具來(lái)解決。而且靜態(tài)工具不需要注入,這樣就避免了循環(huán)依賴(lài)
第一題:
- controller層
@GetMapping("{id}")@ApiOperation(value = "根據(jù)id查詢(xún)用戶(hù)接口")public UserVO queryUserById(@ApiParam("用戶(hù)id") @PathVariable("id") Long id){
// User user = userService.getById(id);
// UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
// return userVO;return userService.queryUserAndAddressById(id);}
- service層
@Overridepublic UserVO queryUserAndAddressById(Long id) {//1. 查詢(xún)用戶(hù)User user = getById(id);if(user == null || user.getStatus() == 2){throw new RuntimeException("用戶(hù)狀態(tài)異常");}//2. 查詢(xún)地址//這里本來(lái)可以直接用本service的lambdaQuery函數(shù) 單位了避免循環(huán)依賴(lài),就使用靜態(tài)工具List<Address> list = Db.lambdaQuery(Address.class).eq(Address::getUserId, user.getId()).list();//3. 封裝vo//3.1 轉(zhuǎn)User的PO為VoUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);//3.2 轉(zhuǎn)地址VOif(CollUtil.isNotEmpty(list)){userVO.setAddressList(BeanUtil.copyToList(list, AddressVO.class));}return userVO;}
第二題:
@Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {//1. 查詢(xún)用戶(hù)List<User> users = listByIds(ids);if(CollUtil.isEmpty(users)){return Collections.emptyList();}//2. 查詢(xún)地址//2.1 獲取用戶(hù)id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());//2.2 根據(jù)用戶(hù)id查詢(xún)地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();//2.3 轉(zhuǎn)換地址VOList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);//2.4 用戶(hù)地址集合分組處理、相同用戶(hù)的放入一個(gè)集合(組)中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if(CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}//3. 轉(zhuǎn)換為VO返回List<UserVO> list = new ArrayList<>(users.size());for(User user:users){//3.1 轉(zhuǎn)換user的PO為VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);list.add(userVO);//3.2 轉(zhuǎn)換為VOuserVO.setAddressList(addressMap.get(user.getId()));}return list;}
也就是靜態(tài)工具的使用和IService一樣,只不過(guò)多了個(gè)實(shí)體類(lèi)傳入。當(dāng)一個(gè)serivice中需要多個(gè)service時(shí),就是用靜態(tài)工具,用法類(lèi)似,也有普通函數(shù)以及復(fù)雜條件查詢(xún)函數(shù),比如Db.lambdaQuery,Db.Db.lambdaUpdate。
1.3 邏輯刪除
邏輯刪除就是基于代碼邏輯模擬刪除效果,但并不會(huì)真正刪除數(shù)據(jù)。思路如下:
- 在表中添加一個(gè)字段標(biāo)記數(shù)據(jù)是否被刪除
- 當(dāng)刪除數(shù)據(jù)時(shí)把標(biāo)記置為1
- 查詢(xún)時(shí)只查詢(xún)標(biāo)記為0的數(shù)據(jù)
MybatisPlus提供了邏輯刪除技能,無(wú)需改變方法調(diào)用的方法,而是在底層幫我們自動(dòng)修改CRUD的語(yǔ)句。我們要做的就是在applicaiton.yaml文件中配置邏輯刪除的字段名稱(chēng)和值即可。
@Autowiredprotected AddressService addressService;@Testvoid testLogicDelete(){//1. 刪除addressService.removeById(59L);//2. 查詢(xún)Address address = addressService.getById(59L);System.out.println("address = " + address);}
雖然使用的remove方法,但是實(shí)際執(zhí)行的是update方法。且在數(shù)據(jù)庫(kù)中還存在,只不過(guò)deleted變成了true
1.4 枚舉處理器
User類(lèi)中有一個(gè)用戶(hù)狀態(tài)字段:
使用枚舉表示狀態(tài),可以提高可讀性。但是問(wèn)題來(lái)了
數(shù)據(jù)庫(kù)中status仍然是int類(lèi)型,這就需要一個(gè)枚舉類(lèi)型到int類(lèi)型的轉(zhuǎn)化。
幸運(yùn)的是Mybatis和mp幫我們解決了,如上圖所示。只需要在枚舉類(lèi)中對(duì)應(yīng)的字段上加上@EnumValue注釋 mp就會(huì)自動(dòng)把對(duì)應(yīng)的字段值與數(shù)據(jù)庫(kù)中列匹配
在applicaiton.yml中配置全局枚舉類(lèi)處理器
@JsonValue //用于枚舉的返回值 數(shù)據(jù)庫(kù)返回顯示的值,比如此處是返回desc的值 也可以返回value的值 否則默認(rèn)返回的是枚舉的對(duì)象名 比如此處的NORMAL FROZEN
@Getter
public enum UserStatus {NORMAL(1,"正常"),FROZEN(2,"凍結(jié)"),;@EnumValueprivate final int value;@JsonValue //用于枚舉的返回值 數(shù)據(jù)庫(kù)返回顯示的值private final String desc;UserStatus(int value,String desc){this.value = value;this.desc = desc;}}
1.5 JSON處理器
數(shù)據(jù)庫(kù)的user表中有一個(gè)info
字段,是JSON類(lèi)型:
格式像這樣:
{"age": 20, "intro": "佛系青年", "gender": "male"}
而目前User
實(shí)體類(lèi)中卻是String
類(lèi)型:
這樣一來(lái),我們要讀取info中的屬性時(shí)就非常不方便。如果要方便獲取,info的類(lèi)型最好是一個(gè)Map
或者實(shí)體類(lèi)。
而一旦我們把info
改為對(duì)象
類(lèi)型,就需要在寫(xiě)入數(shù)據(jù)庫(kù)時(shí)手動(dòng)轉(zhuǎn)為String
,再讀取數(shù)據(jù)庫(kù)時(shí),手動(dòng)轉(zhuǎn)換為對(duì)象
,這會(huì)非常麻煩。
意思就是對(duì)象的String字段可以被mp自動(dòng)轉(zhuǎn)換為數(shù)據(jù)庫(kù)中的JSON,但是數(shù)據(jù)庫(kù)中的JSON不能被mp自動(dòng)幫我們轉(zhuǎn)換為String或者相應(yīng)的對(duì)象
因此MybatisPlus提供了很多特殊類(lèi)型字段的類(lèi)型處理器,解決特殊字段類(lèi)型與數(shù)據(jù)庫(kù)類(lèi)型轉(zhuǎn)換的問(wèn)題。例如處理JSON就可以使用JacksonTypeHandler
處理器。
1.5.1.定義實(shí)體
首先,我們定義一個(gè)單獨(dú)實(shí)體類(lèi)來(lái)與info字段的屬性匹配:
代碼如下:
package com.itheima.mp.domain.po;import lombok.Data;@Data
public class UserInfo {private Integer age;private String intro;private String gender;
}
1.5.2.使用類(lèi)型處理器
接下來(lái),將User類(lèi)的info字段修改為UserInfo類(lèi)型,并聲明類(lèi)型處理器:
測(cè)試可以發(fā)現(xiàn),所有數(shù)據(jù)都正確封裝到UserInfo當(dāng)中了:
同時(shí),為了讓頁(yè)面返回的結(jié)果也以對(duì)象格式返回,我們要修改UserVO中的info字段:
此時(shí),在頁(yè)面查詢(xún)結(jié)果如下:
注意: 由于User類(lèi)中嵌套了UserInfo類(lèi),出現(xiàn)了類(lèi)的嵌套,那么就需要定義復(fù)雜的ResultMap,但是我們又不想定義,那么就直接在User類(lèi)上加注釋,autoResultMap = true 如下圖所示
1.6 配置加密(選學(xué))
目前我們配置文件中的很多參數(shù)都是明文,如果開(kāi)發(fā)人員發(fā)生流動(dòng),很容易導(dǎo)致敏感信息的泄露。所以MybatisPlus支持配置文件的加密和解密功能。
我們以數(shù)據(jù)庫(kù)的用戶(hù)名和密碼為例。
1.6.1.生成秘鑰
首先,我們利用AES工具生成一個(gè)隨機(jī)秘鑰,然后對(duì)用戶(hù)名、密碼加密:
package com.itheima.mp;import com.baomidou.mybatisplus.core.toolkit.AES;
import org.junit.jupiter.api.Test;class MpDemoApplicationTests {@Testvoid contextLoads() {// 生成 16 位隨機(jī) AES 密鑰String randomKey = AES.generateRandomKey();System.out.println("randomKey = " + randomKey);// 利用密鑰對(duì)用戶(hù)名加密String username = AES.encrypt("root", randomKey);System.out.println("username = " + username);// 利用密鑰對(duì)用戶(hù)名加密String password = AES.encrypt("MySQL123", randomKey);System.out.println("password = " + password);}
}
打印結(jié)果如下:
randomKey = 6234633a66fb399f
username = px2bAbnUfiY8K/IgsKvscg==
password = FGvCSEaOuga3ulDAsxw68Q==
1.6.2.修改配置
修改application.yaml文件,把jdbc的用戶(hù)名、密碼修改為剛剛加密生成的密文:
spring:datasource:url: jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=truedriver-class-name: com.mysql.cj.jdbc.Driverusername: mpw:QWWVnk1Oal3258x5rVhaeQ== # 密文要以 mpw:開(kāi)頭password: mpw:EUFmeH3cNAzdRGdOQcabWg== # 密文要以 mpw:開(kāi)頭
1.6.3.測(cè)試
在啟動(dòng)項(xiàng)目的時(shí)候,需要把剛才生成的秘鑰添加到啟動(dòng)參數(shù)中,像這樣:
–mpw.key=6234633a66fb399f
單元測(cè)試的時(shí)候不能添加啟動(dòng)參數(shù),所以要在測(cè)試類(lèi)的注解上配置:
然后隨意運(yùn)行一個(gè)單元測(cè)試,可以發(fā)現(xiàn)數(shù)據(jù)庫(kù)查詢(xún)正常。
2 插件功能
MyBatisPlus提供的內(nèi)置攔截器有下面這些
2.1 分頁(yè)插件
在未引入分頁(yè)插件的情況下,MybatisPlus
是不支持分頁(yè)功能的,IService
和BaseMapper
中的分頁(yè)方法都無(wú)法正常起效。 所以,我們必須配置分頁(yè)插件。
- 首先,要在配置類(lèi)中注冊(cè)MyBatisPlus的核心插件,同時(shí)添加分頁(yè)插件
- 接著,就可以使用分頁(yè)的API了
void testPageQuery(){int pageNo = 1;int pageSize = 2;//1. 準(zhǔn)備分頁(yè)條件//1.1 分頁(yè)條件Page<User> page = Page.of(pageNo, pageSize);//1.2 排序條件 可以指定多個(gè) 比如下面 余額相同則按id排序page.addOrder(new OrderItem("balance",true));page.addOrder(new OrderItem("id",true));//2. 分頁(yè)查詢(xún)Page<User> p = userService.page(page);//3. 解析long total = p.getTotal(); //總條數(shù)System.out.println("total = " + total);long pages = p.getPages(); //總頁(yè)數(shù)System.out.println("pages = " + pages);List<User> records = p.getRecords(); //分頁(yè)數(shù)據(jù)records.forEach(System.out::println);}
2.2 通用分頁(yè)實(shí)體
2.2.1 簡(jiǎn)單分頁(yè)查詢(xún)
案例:簡(jiǎn)單分頁(yè)查詢(xún)案例
需求:遵循下面的接口規(guī)范,編寫(xiě)一個(gè)UserController接口,實(shí)現(xiàn)User的分頁(yè)查詢(xún)
- 實(shí)體類(lèi)
public class PageQuery {@ApiModelProperty("頁(yè)碼")private Integer pageNo;@ApiModelProperty("頁(yè)大小")private Integer pageSize;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc;
}@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel(description = "用戶(hù)查詢(xún)條件實(shí)體")
public class UserQuery extends PageQuery{@ApiModelProperty("用戶(hù)名關(guān)鍵字")private String name;@ApiModelProperty("用戶(hù)狀態(tài):1-正常,2-凍結(jié)")private Integer status;@ApiModelProperty("余額最小值")private Integer minBalance;@ApiModelProperty("余額最大值")private Integer maxBalance;
}
- controller
@GetMapping("/page")@ApiOperation(value = "根據(jù)條件分頁(yè)查詢(xún)用戶(hù)接口")public PageDTO<UserVO> queryUsersPage(UserQuery query){return userService.queryUsersPage(query);}
- impl層
@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();//1. 構(gòu)建查詢(xún)條件Page<User> page = Page.of(query.getPageNo(), query.getPageSize());if(StrUtil.isNotBlank(query.getSortBy())){//不為空page.addOrder(new OrderItem(query.getSortBy(),query.getIsAsc()));}else {//為空 默認(rèn)按照更新時(shí)間排序page.addOrder(new OrderItem("update_time",false));}//2. 分頁(yè)查詢(xún)Page<User> p = 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).page(page);//3. 封裝VO結(jié)果PageDTO<UserVO> userVOPageDTO = new PageDTO<>();userVOPageDTO.setTotal(p.getTotal());userVOPageDTO.setPages(p.getPages());List<User> records = p.getRecords();if(CollUtil.isEmpty(records)){userVOPageDTO.setList(Collections.emptyList());}else {List<UserVO> userVOS = BeanUtil.copyToList(records, UserVO.class);userVOPageDTO.setList(userVOS);}//4. 返回return userVOPageDTO;}
2.2.2 通用分頁(yè)查詢(xún)
- 改造PageQuery類(lèi)
@Data
@ApiModel(description = "分頁(yè)查詢(xún)實(shí)體")
public class PageQuery {@ApiModelProperty("頁(yè)碼")private Integer pageNo = 1;@ApiModelProperty("頁(yè)大小")private Integer pageSize = 5;@ApiModelProperty("排序字段")private String sortBy;@ApiModelProperty("是否升序")private Boolean isAsc = true;public <T> Page<T> toMpPage(OrderItem ... orders){// 1.分頁(yè)條件Page<T> p = Page.of(pageNo, pageSize);// 2.排序條件// 2.1.先看前端有沒(méi)有傳排序字段if (sortBy != null) {p.addOrder(new OrderItem(sortBy, isAsc));return p;}// 2.2.再看有沒(méi)有手動(dòng)指定排序字段if(orders != null){p.addOrder(orders);}return p;}public <T> Page<T> toMpPage(String defaultSortBy, boolean isAsc){return this.toMpPage(new OrderItem(defaultSortBy, isAsc));}public <T> Page<T> toMpPageDefaultSortByCreateTimeDesc() {return toMpPage("create_time", false);}public <T> Page<T> toMpPageDefaultSortByUpdateTimeDesc() {return toMpPage("update_time", false);}
}
- 改造PageDTO類(lèi)
@Data
@ApiModel(description = "分頁(yè)結(jié)果")
@AllArgsConstructor
@NoArgsConstructor
public class PageDTO<T> {@ApiModelProperty("總條數(shù)")private Long total;@ApiModelProperty("總頁(yè)數(shù)")private Long pages;@ApiModelProperty("總數(shù)據(jù)")private List<T> list;/*** 返回空分頁(yè)結(jié)果* @param p MybatisPlus的分頁(yè)結(jié)果* @param <V> 目標(biāo)VO類(lèi)型* @param <P> 原始PO類(lèi)型* @return VO的分頁(yè)對(duì)象*/public static <V, P> PageDTO<V> empty(Page<P> p){return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());}/*** 將MybatisPlus分頁(yè)結(jié)果轉(zhuǎn)為 VO分頁(yè)結(jié)果* @param p MybatisPlus的分頁(yè)結(jié)果* @param voClass 目標(biāo)VO類(lèi)型的字節(jié)碼* @param <V> 目標(biāo)VO類(lèi)型* @param <P> 原始PO類(lèi)型* @return VO的分頁(yè)對(duì)象*/public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {// 1.非空校驗(yàn)List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 無(wú)數(shù)據(jù),返回空結(jié)果return empty(p);}// 2.數(shù)據(jù)轉(zhuǎn)換List<V> vos = BeanUtil.copyToList(records, voClass);// 3.封裝返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}/*** 將MybatisPlus分頁(yè)結(jié)果轉(zhuǎn)為 VO分頁(yè)結(jié)果,允許用戶(hù)自定義PO到VO的轉(zhuǎn)換方式* @param p MybatisPlus的分頁(yè)結(jié)果* @param convertor PO到VO的轉(zhuǎn)換函數(shù)* @param <V> 目標(biāo)VO類(lèi)型* @param <P> 原始PO類(lèi)型* @return VO的分頁(yè)對(duì)象*/public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {// 1.非空校驗(yàn)List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 無(wú)數(shù)據(jù),返回空結(jié)果return empty(p);}// 2.數(shù)據(jù)轉(zhuǎn)換List<V> vos = records.stream().map(convertor).collect(Collectors.toList());// 3.封裝返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}}
- impl層
@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();//1. 構(gòu)建查詢(xún)條件Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();Page<User> p = lambdaQuery().like(name != null, User::getUsername, name).eq(status != null, User::getStatus, status).page(page);return PageDTO.of(page, UserVO.class);}