擴展功能
- 一、靜態工具
- 二、邏輯刪除
- 三、通用枚舉
- 1、定義枚舉
- 2、配置枚舉處理器
- 3、測試
- 四、JSON類型處理器
- 1、定義實體
- 2、使用類型處理器
- 五、分頁
- 1、配置分頁插件
- 2、分頁API
- 3、示例
一、靜態工具
有的時候Service之間也會相互調用,為了避免出現循環依賴問題,MybatisPlus提供一個靜態工具類:Db,其中的一些靜態方法與IService中方法簽名基本一致,也可以幫助我們實現CRUD功能:
實例:
@Test
void testDbGet() {User user = Db.getById(1L, User.class);System.out.println(user);
}@Test
void testDbList() {// 利用Db實現復雜條件查詢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");
}
傳入實體類的class字節碼,拿到字節碼就能通過反射拿到實體類的相關信息,從而拿到注解上的信息諸如類名、表名等,以此實現CURD。
Db靜態類其實很原始service的方法使用基本一致,只是多了一個實體類的class字節碼。
二、邏輯刪除
對于一些比較重要的數據,我們往往會采用邏輯刪除的方案,即:
- 在表中添加一個字段標記數據是否被刪除
- 當刪除數據時把標記置為true
- 查詢時過濾掉標記為true的數據
一旦采用了邏輯刪除,所有的查詢和刪除邏輯都要跟著變化,非常麻煩。
為了解決這個問題,MybatisPlus就添加了對邏輯刪除的支持。
只有MybatisPlus生成的SQL語句才支持自動的邏輯刪除,自定義SQL需要自己手動處理邏輯刪除。
例如,我們給address表添加一個邏輯刪除字段:
alter table address add deleted bit default b'0' null comment '邏輯刪除';
然后給Address實體添加deleted字段:
接下來,我們要在application.yml中配置邏輯刪除字段:
mybatis-plus:global-config:db-config:logic-delete-field: deleted # 全局邏輯刪除的實體字段名(since 3.3.0,配置后可以忽略不配置步驟2)logic-delete-value: 1 # 邏輯已刪除值(默認為 1)logic-not-delete-value: 0 # 邏輯未刪除值(默認為 0)
測試:
首先,我們執行一個刪除操作:
@Test
void testDeleteByLogic() {// 刪除方法與以前沒有區別addressService.removeById(59L);
}
方法與普通刪除一模一樣,但是底層的SQL邏輯變了:
查詢一下試試:
@Test
void testQuery() {List<Address> list = addressService.list();list.forEach(System.out::println);
}
會發現id為59的確實沒有查詢出來,而且SQL中也對邏輯刪除字段做了判斷:
綜上, 開啟了邏輯刪除功能以后,我們就可以像普通刪除一樣做CRUD,基本不用考慮代碼邏輯問題。還是非常方便的。
注意:
邏輯刪除本身也有自己的問題,比如:
- 會導致數據庫表垃圾數據越來越多,從而影響查詢效率
- SQL中全都需要對邏輯刪除字段做判斷,影響查詢效率
因此,我不太推薦采用邏輯刪除功能,如果數據不能刪除,可以采用把數據遷移到其它表的辦法。
三、通用枚舉
User類中有一個用戶狀態字段:
像這種字段我們一般會定義一個枚舉,做業務判斷的時候就可以直接基于枚舉做比較。但是我們數據庫采用的是int類型,對應的PO也是Integer。因此業務操作時必須手動把枚舉與Integer轉換,非常麻煩。
因此,MybatisPlus提供了一個處理枚舉的類型轉換器,可以幫我們把枚舉類型與數據庫類型自動轉換。
1、定義枚舉
我們定義一個用戶狀態的枚舉:
package com.itheima.mp.enums;import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;@Getter
public enum UserStatus {NORMAL(1, "正常"),FREEZE(2, "凍結");private final int value;private final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;}
}
然后把User類中的status字段改為UserStatus 類型:
要讓MybatisPlus處理枚舉與數據庫類型自動轉換,我們必須告訴MybatisPlus,枚舉中的哪個字段的值作為數據庫值。
MybatisPlus提供了@EnumValue 注解來標記枚舉屬性:
2、配置枚舉處理器
在application.yaml文件中添加配置:
mybatis-plus:configuration:default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
3、測試
@Test
void testService() {List<User> list = userService.list();list.forEach(System.out::println);
}
同時,為了使頁面查詢結果也是枚舉格式,我們需要修改UserVO中的status屬性:
并且,在UserStatus枚舉中通過@JsonValue注解標記JSON序列化時展示的字段:
最后,在頁面查詢,結果如下:
四、JSON類型處理器
數據庫的user表中有一個info字段,是JSON類型:
格式像這樣:
{"age": 20, "intro": "佛系青年", "gender": "male"}
而目前User實體類中卻是String類型:
這樣一來,我們要讀取info中的屬性時就非常不方便。如果要方便獲取,info的類型最好是一個Map或者實體類。
而一旦我們把info改為對象類型,就需要在寫入數據庫時手動轉為String,再讀取數據庫時,手動轉換為對象,這會非常麻煩。
因此MybatisPlus提供了很多特殊類型字段的類型處理器,解決特殊字段類型與數據庫類型轉換的問題。例如處理JSON就可以使用JacksonTypeHandler處理器。
1、定義實體
我們定義一個單獨實體類來與info字段的屬性匹配:
package com.itheima.mp.domain.po;import lombok.Data;@Data
public class UserInfo {private Integer age;private String intro;private String gender;
}
2、使用類型處理器
接下來,將User類的info字段修改為UserInfo類型,并聲明類型處理器:
同時,在User類上添加一個注解,聲明自動映射:
測試可以發現,所有數據都正確封裝到UserInfo當中了:
五、分頁
在未引入分頁插件的情況下,MybatisPlus是不支持分頁功能的,IService和BaseMapper中的分頁方法都無法正常起效。
所以,我們必須配置分頁插件。
1、配置分頁插件
在項目中新建一個配置類:
其代碼如下:
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 {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {// 初始化核心插件MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 添加分頁插件interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return interceptor;}
}
2、分頁API
編寫一個分頁查詢的測試:
@Test
void testPageQuery() {// 1.分頁查詢,new Page()的兩個參數分別是:頁碼、每頁大小Page<User> p = userService.page(new Page<>(2, 2));// 2.總條數System.out.println("total = " + p.getTotal());// 3.總頁數System.out.println("pages = " + p.getPages());// 4.數據List<User> records = p.getRecords();records.forEach(System.out::println);
}
這里用到了分頁參數,Page,即可以支持分頁參數,也可以支持排序參數。常見的API如下:
int pageNo = 1, pageSize = 5;
// 分頁參數
Page<User> page = Page.of(pageNo, pageSize);
// 排序參數, 通過OrderItem來指定
page.addOrder(new OrderItem("balance", false));userService.page(page);
3、示例
現在要實現一個用戶分頁查詢的接口,接口規范如下:
controller.java
package com.itheima.mp.controller;import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.query.PageQuery;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("users")
@RequiredArgsConstructor
public class UserController {private final UserService userService;@GetMapping("/page")public PageDTO<UserVO> queryUsersPage(UserQuery query){return userService.queryUsersPage(query);}// 。。。 略
}
然后在IUserService中創建queryUsersPage方法:
PageDTO<UserVO> queryUsersPage(PageQuery query);
接下來,在UserServiceImpl中實現該方法:
@Override
public PageDTO<UserVO> queryUsersPage(PageQuery query) {// 1.構建條件// 1.1.分頁條件Page<User> page = Page.of(query.getPageNo(), query.getPageSize());// 1.2.排序條件if (query.getSortBy() != null) {page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));}else{// 默認按照更新時間排序page.addOrder(new OrderItem("update_time", false));}// 2.查詢page(page);// 3.數據非空校驗List<User> records = page.getRecords();if (records == null || records.size() <= 0) {// 無數據,返回空結果return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());}// 4.有數據,轉換List<UserVO> list = BeanUtil.copyToList(records, UserVO.class);// 5.封裝返回return new PageDTO<UserVO>(page.getTotal(), page.getPages(), list);
}