?作者簡介:大家好,我是 Meteors., 向往著更加簡潔高效的代碼寫法與編程方式,持續分享Java技術內容。
🍎個人主頁:Meteors.的博客
💞當前專欄:知識分享、知識備份
?特色專欄: 知識分享
🥭本文內容:MyBatisPlus——入門到進階
📚 ** ps ** ?: 閱讀文章如果有問題或者疑惑,歡迎在評論區提問或指出。
目錄
01. 入門
1. 添加依賴
2. Mapper繼承BaseMapper接口
02. 常見注解
1. 說明
?2. 例子
03. 常見配置
03. 常見配置
04. 核心功能-條件構造器
1. 說明
2. 例子
05. 核心功能-自定義SQL
1. 說明
2. 步驟
a. 基于Wrapper構建where條件
b. 在mapper方法中用Param注解聲明wrapper變量名稱,必須是ew
c.定義SQL,并使用Wrapper條件
06. 核心功能-Service接口
1. 說明
2. 步驟
a. Service層繼承IService接口
b. Service實現類繼承(Impl)繼承ServiceImpl
c. 調用方法(以save為例,更多例子點擊這里)
3. 補充
07. 擴展功能-代碼生成?
方式一:使用代碼生成器代碼進行生成
方式二:使用MybatisX進行代碼生成(這個會少一個controller)
方式三:使用應用商店的MybatisPlus插件進行代碼生成
08. 擴展功能-靜態工具
1.說明
2. 例子
09. 擴展功能-邏輯刪除
1. 說明
2. 例子
3. 補充(引用自黑馬視頻)
10.? 擴展功能-枚舉處理器
1. 說明
2. 例子
11. 擴展功能-JSON處理器
1. 說明
2. 例子
?12. 插件功能-分頁插件
1. 說明
2. 例子
01. 入門
1. 添加依賴
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.3.1</version>-->
<!-- </dependency>--><!-- MyBatisPlus依賴 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.6</version></dependency>
2. Mapper繼承BaseMapper接口
// 類似于
public interface UserMapper extend BaeMapper<User>{}
02. 常見注解
1. 說明
mp通過掃描實體類,并基于反射獲取實體類信息作為數據庫表信息,比較常用的注解如下:
- @TableName:指定表名
- @TableId:用來指定表中的主鍵字段信息
- idType枚舉說明:
- ATUO:數據庫自增
- INPUT:通過set方法自行輸入
- ASSUGN_ID:分配ID(接口IdentifierGenerator的方法nextId來生成id,默認實現類為DefaultIdentifierGenerator雪花算法)
- @TableField:用來指定表中的普通字段信息
- 使用場景:
- 成員變量名宇數據庫字段名不一致
- 成員變量名以is開頭,且是布爾值
- 成員變量名宇數據庫關鍵字沖突
- 成員變量不是數據庫字段
?2. 例子
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;import java.time.LocalDateTime;@Data
@TableName("tb_user")
public class User {/*** 用戶id*/@TableId(type = IdType.AUTO)private Long id;/*** 用戶名*/@TableField("`username`")private String username;/*** 密碼*/@TableField(exist = false)private String password;/*** 注冊手機號*/private String phone;/*** 詳細信息*/private String info;/*** 使用狀態(1正常 2凍結)*/private Integer status;/*** 賬戶余額*/private Integer balance;/*** 創建時間*/private LocalDateTime createTime;/*** 更新時間*/private LocalDateTime updateTime;
}
03. 常見配置
03. 常見配置
mybatis-plus:configuration:# MyBatis 配置map-underscore-to-camel-case: true# 掃描包global-config:# 全局配置db-config:# 數據庫配置id-type: auto# 包的位置type-aliases-package: com.itmeteors.mp.domain.po
04. 核心功能-條件構造器
1. 說明
mp為我們提供了的條件構造器方法,以便與我們進行查詢操作:
2. 例子
// 01. 查詢出名字中帶o的,存款大于1000元人的id、userName、info、balance@Testvoid testQueryWrapper(){// 1. 構建查詢條件QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "username", "info", "balance").like("username", "o").ge("balance", 1000);// 2. 查詢List<User> users = userMapper.selectList(wrapper);users.forEach(System.out::println);}// 02. 更新用戶名為jack的用戶的余額為2000@Testvoid testUpdateByQueryWrapper(){// 1. 要更新的數據User user = new User;user.setBalance(2000);// 2. 更新要的條件QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username", "jack");// 3. 執行更新userMapper.update(user,wrapper);}// 03. 更新id為1,2,4的用戶的余額,扣200@Testvoid testUpdateWrapper(){List<Long> ids = List.of(1L,2L,4L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance - 200").in("id",ids);userMapper.update(null,wrapper);}// 04. 使用lambada方式更新id為1,2,4的用戶的余額,扣200@Testvoid testLambadaUpdateWrapper(){List<Long> ids = List.of(1L,2L,4L);LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<User>().setSql("balance = balance - 200").in(User::getId,ids);userMapper.update(null,wrapper);}
05. 核心功能-自定義SQL
1. 說明
利用mp來構建復雜的Where條件,然后自己定義SQL語句中剩下的部分。
2. 步驟
a. 基于Wrapper構建where條件
@Testvoid testUpdateWrapper2(){// 1. 更新條件List<Long> ids = List.of(1L, 2L, 4L);int amount = 200;// 2. 定義條件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);// 3. 調用自定義SQL方法userMapper.updateBalanceByIds(wrapper,amount);}
b. 在mapper方法中用Param注解聲明wrapper變量名稱,必須是ew
void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);
c.定義SQL,并使用Wrapper條件
<update id="updateBalanceByIds">UPDATE tb_user SET balance = balance - #{amount} ${ew.customSqlSegment}</update>
06. 核心功能-Service接口
1. 說明
mp的service接口為我們定義了很多增刪改查的方法,減少了在service層的代碼書寫量
2. 步驟
a. Service層繼承IService接口
public interface IUserService extends IService<User> {}
b. Service實現類繼承(Impl)繼承ServiceImpl
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {}
c. 調用方法(以save為例,更多例子點擊這里)
@SpringBootTest
class IUserServiceTest {@Autowiredprivate IUserService userService;@Testvoid testSaveUser(){User user = new User();user.setUsername("tesst");user.setPassword("123");user.setPhone("15966666666");user.setBalance(200);user.setInfo("{\"age\": 24, \"intro\": \"英文老師\", \"gender\": \"female\"}");user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userService.save(user);}
}
3. 補充
使用?saveBatch()方法?默認不生效:
原因:雖然執行了批量插入方法,但是語句未被合并并優化,然后還是多個請求單獨發送給MySQL服務器,網絡往返次數和服務器解析SQL語句的開銷未減少
解決方法:在yml的數據庫連接url最后加上:?rewriteBatchedStatements=true,允許驅動程序重寫批處理語句為單個字符串,并發送給服務器,從而提高插入、更新和刪除操作的速度。
當然,這樣可能也會有缺點:
- SQL注入風險:因為語句被重寫,所以可能會增加SQL注入的風險。務必確保你使用的SQL語句是安全的,避免直接拼接用戶輸入。
- 不支持所有SQL語法:某些復雜的SQL語法可能不支持重寫。
- 調試困難:由于語句被重寫,所以在調試時可能更難以確定問題所在。
07. 擴展功能-代碼生成?
方式一:使用代碼生成器代碼進行生成
步驟:
- 導入依賴
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.6</version> </dependency>
- 配置生成器類
public class CodeGenerator {public static void main(String[] args) {FastAutoGenerator.create("jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true", "root", "****").globalConfig(builder -> {builder.author("Meteors") // 設置作者.enableSwagger() // 開啟 swagger 模式.outputDir("C:\\Users\\Meteors\\Desktop\\SpringCloud微服務—資料\\day01-MybatisPlus\\資料\\mp-demo\\src\\main\\java"); // 指定輸出目錄}).dataSourceConfig(builder ->builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {int typeCode = metaInfo.getJdbcType().TYPE_CODE;if (typeCode == Types.SMALLINT) {// 自定義類型轉換return DbColumnType.INTEGER;}return typeRegistry.getColumnType(metaInfo);})).packageConfig(builder ->builder.parent("com.itheima.mp") // 設置父包名.entity("domain.po") // 設置實體類包名.pathInfo(Collections.singletonMap(OutputFile.xml, "C:\\Users\\Meteors\\Desktop\\SpringCloud微服務—資料\\day01-MybatisPlus\\資料\\mp-demo\\src\\main\\resources\\mapper")) // 設置mapperXml生成路徑).strategyConfig(builder ->builder.addInclude("address") // 設置需要生成的表名.addTablePrefix("t_", "c_") // 設置過濾表前綴.entityBuilder().enableLombok() // 啟用 Lombok.enableTableFieldAnnotation() // 啟用字段注解.controllerBuilder().enableRestStyle() // 啟用 REST 風格).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默認的是Velocity引擎模板.execute();} }
- 運行代碼
方式二:使用MybatisX進行代碼生成(這個會少一個controller)
步驟:
- 選擇MybatisX:
- 選擇
方式三:使用應用商店的MybatisPlus插件進行代碼生成
步驟:
- 下載插件
- 重啟idea,并配置數據庫
- 進行代碼生成
08. 擴展功能-靜態工具
1.說明
使用靜態工具可以避免循環依賴問題。
2. 例子
需求(如果普通方式進行實現,下面兩個需求可能會造成循環依賴):
- 改造更具id查詢用戶的接口,查詢用戶的同時,查詢出用戶對應的所有地址
//Controller@ApiOperation("根據id查詢用戶接口")@GetMapping("{id}")public UserVO queryUserById(@ApiParam("用戶id") @PathVariable("id") Long id) {return userService.queryUserAndAddressById(id);} //ServiceUserVO queryUserAndAddressById(Long id); //Impl@Overridepublic UserVO queryUserAndAddressById(Long id) {// 1. 查詢用戶User user = getById(id);if (user == null || user.getStatus() == 2) {throw new RuntimeException("用戶狀態異常!");}// 2. 查詢地址List<Address> addressList = Db.lambdaQuery(Address.class).eq(Address::getUserId, id).list();// 3. 封裝VOUserVO userVO = BeanUtil.copyProperties(user, UserVO.class);if (CollUtil.isNotEmpty(addressList)){userVO.setAddresses(BeanUtil.copyToList(addressList, AddressVO.class));}return userVO;}
- 改造根據id批量查詢用戶的接口,查詢用戶的同時,查詢出用戶對應的所有地址
// Controller@ApiOperation("根據id批量查詢用戶接口")@GetMappingpublic List<UserVO> queryUserByIds(@ApiParam("用戶id集合") @RequestParam("ids") List<Long> ids) {return userService.queryUserAndAddressByIds(ids);} // ServiceList<UserVO> queryUserAndAddressByIds(List<Long> ids); // Impl@Overridepublic List<UserVO> queryUserAndAddressByIds(List<Long> ids) {// 1. 查詢用戶List<User> users = listByIds(ids);if (CollUtil.isEmpty(users)) {return Collections.emptyList();}// 2. 查詢地址// 2.1 獲取用戶id集合List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());// 2.2 根據用戶id查詢地址List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();// 2.3 轉換地址VOList<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);// 2.4 用戶地址集合分組處理,相同用戶的放入一個集合(組)中Map<Long, List<AddressVO>> addressMap = new HashMap<>(0);if (CollUtil.isNotEmpty(addressVOList)) {addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));}// 3. 轉換VO返回List<UserVO> list = new ArrayList<>(users.size());for (User user : users) {// 3.1 轉換User的PO為VOUserVO vo = BeanUtil.copyProperties(user, UserVO.class);// 3.2 轉換地址VOvo.setAddresses(addressMap.get(user.getId()));// 3.3 添加到集合list.add(vo);}return list;}
09. 擴展功能-邏輯刪除
1. 說明
邏輯刪除解釋基于代碼邏輯模擬刪除效果,但并不會真正刪除數據。思路:
- 在表中添加一個字段標記數據是否被刪除
- 當刪除數據時把標記置為1
- 查詢時值查詢標記為0的數據
例如邏輯刪除字段為deleted:
- 刪除操作:UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0;
- 查詢操作:SELECT * FROM? user WHERE?deleted = 0;?
mp提供了邏輯刪除功能,無需改變方法調用的方式,而是在底層幫我們自動修改CRUD的語句。我們要做的就是在application.yaml文件中配置邏輯刪除的字段名稱和值即可:
2. 例子
在使用mp的邏輯刪除功能之后,原有的查詢功能會默認在末尾添加查詢條件,刪除功能會自動變更為UPDATE,下面是具體的例子:
- 代碼
@SpringBootTest class IAddressServiceTest {@Autowiredprivate IAddressService addressService;@Testvoid testLogicDelete(){// 1. 刪除addressService.removeById(59L);// 2. 查詢Address address = addressService.getById(59L);System.out.println("address = " + address);} }
- 結果
3. 補充(引用自黑馬視頻)
邏輯刪除本身也有自己的問題,比如:
- 會導致數據庫表垃圾數據越來越多,影響查詢效率
- SQL中全部需要對邏輯刪除字段做判斷,影響查詢效率
因此,需要進行邏輯刪除時,可以考慮把數據遷移到其它表的方法進行替代
10.? 擴展功能-枚舉處理器
1. 說明
用戶狀態字段如果用例如private Integer status的方式進行表示,用這種類型表示不直觀且代碼且屬于硬編碼,可維護性差、靈活性低、可擴展性差、可讀性差、重用性受限、錯誤分險增加,而使用mp枚舉處理器,可以避免這些缺點。
2. 例子
將原有的狀態字段改為枚舉類型:
- 步驟1:在yaml文件中設置枚舉處理器
- 步驟2:新建枚舉類
@Getter public enum UserStatus {NORMAL(1,"正常"),FROZEN(2,"凍結"),;@EnumValueprivate final int value;@JsonValueprivate final String desc;UserStatus(int value, String desc) {this.value = value;this.desc = desc;} }
- 步驟3:將字段類型替換為枚舉類型
/*** 使用狀態(1正常 2凍結)*/private UserStatus status;
- 結果:
11. 擴展功能-JSON處理器
1. 說明
當數據中的表中含有json類型的字段時,如果自己實現Java代碼實體類與之對應,比較麻煩,此時可以使用mp的json處理器進行實現,提高開發效率。
2. 例子
將一個數據庫中使用json類型的info屬性,使用 json處理讓Java實體類與之對應,步驟:
- 新建那個屬性的實體類
@Data @NoArgsConstructor @AllArgsConstructor(staticName = "of") public class UserInfo {private Integer age;private String intro;private String gender; }
- 字段上定義處理器并在實體類上開啟autoResultMap
ps:
- 賦值方式
//user.setInfo("{\"age\": 24, \"intro\": \"英文老師\", \"gender\": \"female\"}");
user.setInfo(UserInfo.of(24,"英語老師","female"));
- 結果
?12. 插件功能-分頁插件
1. 說明
在mp3.4版本之后,mp已經默認集成了mp插件,但如果需要對分頁的功能(例如溢出總頁數后是否進行處理、單頁條數限制、數據庫類型設置、方言實現設置)等,仍需要新建分頁插件進行實現。
2. 例子
以新建一個的分頁插件和通過分頁實體實現分頁功能為例,步驟如下:
- 添加分頁插件
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();// 1. 創建分頁插件PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);paginationInnerInterceptor.setMaxLimit(1000L);// 2. 添加分頁插件interceptor.addInnerInterceptor(paginationInnerInterceptor);return interceptor;} }
- 新建分頁實體
@Data public class PageQuery {private Integer pageNo;private Integer pageSize;private String sortBy;private Boolean isAsc;public <T> Page<T> toMpPage(OrderItem ... orders){// 1.分頁條件Page<T> p = Page.of(pageNo, pageSize);// 2.排序條件// 2.1.先看前端有沒有傳排序字段if (sortBy != null) {p.addOrder(new OrderItem(sortBy, isAsc));return p;}// 2.2.再看有沒有手動指定排序字段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);} }
- 新建分頁返回實體
@Data @NoArgsConstructor @AllArgsConstructor public class PageDTO<V> {private Long total;private Long pages;private List<V> list;/*** 返回空分頁結果* @param p MybatisPlus的分頁結果* @param <V> 目標VO類型* @param <P> 原始PO類型* @return VO的分頁對象*/public static <V, P> PageDTO<V> empty(Page<P> p){return new PageDTO<>(p.getTotal(), p.getPages(), Collections.emptyList());}/*** 將MybatisPlus分頁結果轉為 VO分頁結果* @param p MybatisPlus的分頁結果* @param voClass 目標VO類型的字節碼* @param <V> 目標VO類型* @param <P> 原始PO類型* @return VO的分頁對象*/public static <V, P> PageDTO<V> of(Page<P> p, Class<V> voClass) {// 1.非空校驗List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 無數據,返回空結果return empty(p);}// 2.數據轉換List<V> vos = BeanUtil.copyToList(records, voClass);// 3.封裝返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);}/*** 將MybatisPlus分頁結果轉為 VO分頁結果,允許用戶自定義PO到VO的轉換方式* @param p MybatisPlus的分頁結果* @param convertor PO到VO的轉換函數* @param <V> 目標VO類型* @param <P> 原始PO類型* @return VO的分頁對象*/public static <V, P> PageDTO<V> of(Page<P> p, Function<P, V> convertor) {// 1.非空校驗List<P> records = p.getRecords();if (records == null || records.size() <= 0) {// 無數據,返回空結果return empty(p);}// 2.數據轉換List<V> vos = records.stream().map(convertor).collect(Collectors.toList());// 3.封裝返回return new PageDTO<>(p.getTotal(), p.getPages(), vos);} }
- 查詢類繼承分頁實體類
@EqualsAndHashCode(callSuper = true) @Data @ApiModel(description = "用戶查詢條件實體") public class UserQuery extends PageQuery{@ApiModelProperty("用戶名關鍵字")private String name;@ApiModelProperty("用戶狀態:1-正常,2-凍結")private Integer status;@ApiModelProperty("余額最小值")private Integer minBalance;@ApiModelProperty("余額最大值")private Integer maxBalance; }
- 實現查詢接口
// Controller@ApiOperation("根據條件分頁查詢用戶接口")@GetMapping("/page")public PageDTO<UserVO> queryUserPage(UserQuery query){return userService.queryUsersPage(query);} // ServicePageDTO<UserVO> queryUsersPage(UserQuery query); // Impl@Overridepublic PageDTO<UserVO> queryUsersPage(UserQuery query) {String name = query.getName();Integer status = query.getStatus();// 1. 構建查詢條件// 1.1 分頁條件Page<User> page = query.toMpPageDefaultSortByUpdateTimeDesc();// 2. 分頁查詢Page<User> p = lambdaQuery().like(name!=null,User::getUsername,name).eq(status!=null,User::getStatus,status).page(page);// 3. 封裝VO結果return PageDTO.of(p,user -> {// 1. 拷貝基礎屬性UserVO vo = BeanUtil.copyProperties(user, UserVO.class);// 2. 處理特殊邏輯vo.setUsername(vo.getUsername() +"**");return vo;});}
?最后,
? ? ? ? 希望文章對你有所幫助!