今天深入的學習了一下mp,從頭開始學習!哈哈哈哈哈
本節只講干的!
我們上來先看一段代碼,不知道你能不能看明白!
package com.itheima.mp.mapper;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.po.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;@SpringBootTest
class UserMapperTest {@Autowiredprivate UserMapper userMapper;@Testvoid testInsert() {User user = new User();
// user.setId(5L);user.setUsername("hanMeiMei");user.setPassword("123");user.setPhone("18688990011");user.setBalance(200);user.setInfo(UserInfo.of(24, "英文老師", "female"));user.setCreateTime(LocalDateTime.now());user.setUpdateTime(LocalDateTime.now());userMapper.insert(user);}@Testvoid testSelectById() {User user = userMapper.selectById(5L);System.out.println("user = " + user);}@Testvoid testQueryByIds() {List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));users.forEach(System.out::println);}@Testvoid testUpdateById() {User user = new User();user.setId(5L);user.setBalance(20000);userMapper.updateById(user);}@Testvoid testDeleteUser() {userMapper.deleteById(5L);}@Testvoid testQuery() {//1.構造查詢條件QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>().select("id", "username", "info", "balance").like("username", "o").ge("balance", 1000);//查詢List<User> users = userMapper.selectList(userQueryWrapper);System.out.println("users = " + users);}@Testvoid testUpdate() {//1.要更新的數據User user = new User();user.setBalance(2000);// 2. 構造更新條件QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>().eq("username", "jack");userMapper.update(user, userQueryWrapper);}@Testvoid testUpdateWrapper() {//1.List<Long> ids = new ArrayList<>();ids.add(1L);ids.add(2L);ids.add(4L);UpdateWrapper<User> wrapper = new UpdateWrapper<User>().setSql("balance = balance-200").in("id", ids);//3.userMapper.update(null, wrapper);}@Testvoid testCustomSqlUpdate() {List<Long> ids = new ArrayList<>();ids.add(1L);ids.add(2L);ids.add(4L);int amount = 200;//2.定義條件QueryWrapper<User> wrapper = new QueryWrapper<User>().in("id", ids);userMapper.updateBalance(wrapper, amount);}@Testvoid testCustomSqlSelect() {QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>().eq("username", "jack").select("id", "username", "info", "balance");userMapper.selectList(userQueryWrapper);}
}
這是一個測試類,里面寫的都是mapper的代碼!大概解釋一下,就是mp既可以用在service層,也可以用在mapper層,但是呢,公司常用的是mapper層的東西,我只是寫著玩一下mapper層的東西,熟悉一下!mapper的查詢用的是querywrapper,可以用mapper進行增刪改查操作!
我們一直用mapper層進行增上改查的操作,其實很多公司是并不允許的,所以我們選擇用service層進行操作!
package com.itheima.mp.service;import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.mp.domain.dto.PageDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.query.PageQuery;
import com.itheima.mp.domain.query.UserQuery;
import com.itheima.mp.domain.vo.UserVO;import java.util.List;public interface IUserService extends IService<User> {void deduchMoney(Long id, Integer money);List<User> quertUsers(String name, Integer status, Integer minBalance, Integer maxBalance);void updateUserId(Long id, Integer money);UserVO queryUserAndAddressById(Long id);List<UserVO> queryUserAndAddressByIds(List<Long> ids);PageDTO<UserVO> queryUserAndAddressByPage(UserQuery page);
}
首先,我們的service層需要繼承IService這個東西,然后寫上實體類,這樣就可以利用反射查詢到你的數據庫!這樣就可以用mp的一些操作了
我們先來簡單的解釋一些代碼!
@PostMapping@ApiOperation("新增用戶信息")public void saveUser(@RequestBody UserFormDTO userFormDTO) {//1.把dto拷貝到實體po對象User user = BeanUtil.copyProperties(userFormDTO, User.class);userService.save(user);}
save這個一看就是保存操作,用service進行一個保存的操作,前端傳過來DTO,然后后端進行存儲,直接這樣就可以,很方便~
@DeleteMapping("{id}")@ApiOperation("刪除用戶信息")public void deleteUserById(@ApiParam("用戶id") @PathVariable Long id) {userService.removeById(id);}
removeById這個就是根據id刪除操作,很簡答!
@GetMapping("{id}")@ApiOperation("按照id查詢用戶信息")public UserVO SelectUserById(@ApiParam("用戶id") @PathVariable Long id) {userService.getById(id);}
這個就是根據單個id查詢用戶操作!
@GetMapping@ApiOperation("查詢用戶信息")public List<UserVO> SelectQueryUserById(@ApiParam("用戶id集合") @RequestParam("ids") List<Long> ids) {List<User> users = userService.listByIds(ids);return BeanUtil.copyToList(users, UserVO.class);}
這個就是根據id集合然后去查詢你的用戶信息操作!
然后我們上點難度奧!不能總弄那個增刪改查!
需求:前端傳過來一個id和money,要求讓這個id的用戶扣錢!
所以我們controller層就不能直接用mp自帶的操作了,這時候我們需要自定義一個service去寫!
@PutMapping("/{id}/deduction")@ApiOperation("扣減用戶余額")public void updateUserId(@ApiParam("用戶id") @PathVariable Long id,@ApiParam("金額") @PathVariable Integer money) {userService.updateUserId(id, money);}
這樣我們就拿到了id和money,然后去更新操作,我們看一下Impl實現類!
@Override@Transactionalpublic void updateUserId(Long id, Integer money) {//1.查詢用戶User user = this.getById(id);//2.校驗狀態if (user == null || user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用戶不存在或者被凍結");}//3.校驗余額是否充足if (user.getBalance() < money) {throw new RuntimeException("余額不足");}//4.減去余額int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, UserStatus.FROZEN).eq(User::getId, id).update();}
我們先利用id去查詢這個用戶,然后對這個用戶進行校驗,還要對金額進行一個校驗!
最后我們在減去剩余的錢,然后set到這個用戶的balance里面!
這里面大家可能看著有一點蒙,這兩個位置我需要解釋一下,第一個condition,就是條件,這里可以實現一個操作!就是當剩余的金額是0的時候,我們的狀態就要變成凍結了!因為他錢都不夠了,肯定凍結他。這里還涉及到一個操作就是mp對枚舉類型的操作!
我們的status這種一般就會定義出來一個枚舉,因為只有2種狀態,所以定義枚舉是一個非常好的選擇!我們定義完枚舉后,我們還需要去對應的po里面把這個字段的類型改了!
處理枚舉與數據庫類型自動轉換,我們必須告訴mp
,枚舉中的哪個字段的值作為數據庫值。mp提供了@EnumValue
注解來標記枚舉屬性,然后我們還需要去yml里面去配置一下!
這樣我們查詢出來的就是枚舉的類型了,但是我們想查出來desc的值,這個mp也提供了一個注解
紅色箭頭:標注為mp被數據庫識別的屬性。藍色箭頭:標注為返回的字段!
這樣的一個更新的操作基本上就完成了!我們再看一下完整代碼
@Override@Transactionalpublic void updateUserId(Long id, Integer money) {//1.查詢用戶User user = this.getById(id);//2.校驗狀態if (user == null || user.getStatus() == UserStatus.FROZEN) {throw new RuntimeException("用戶不存在或者被凍結");}//3.校驗余額是否充足if (user.getBalance() < money) {throw new RuntimeException("余額不足");}//4.減去余額int remainBalance = user.getBalance() - money;lambdaUpdate().set(User::getBalance, remainBalance).set(remainBalance == 0, User::getStatus, UserStatus.FROZEN).eq(User::getId, id).update();}
再來一個新的需求,我們常常能碰到這樣的事情,就是用戶進行搜索,它可以輸入3-4個字段,但是用戶又不一定全填,所以我們就需要做好幾個的非空判斷的模糊查詢!這樣比較煩!我們來用mp實現一下這個效果!
我們直接看一下service,這里就是前面是條件,然后后面是查詢出來的東西,這樣就可以實現這個操作了!非常簡單!
接下來我就又碰到了一個問題,就是我們有些字段可能是json的,但是我們實體類里面寫的都是String類型,所以這樣就導致我們在service里面拿不到這個值!mp也提供了這樣的方法!
我們首先定義一個實體類去裝這個json對象!
然后在實體類里面需要添加2個注解,一個是@TableName,另一個是@TableField
這樣就看到,我們的json數據可以被正常的返回了!
現在又有一個新的需求!
我們有兩張表,一張是用戶表,一張是地址表!
這里面能看到我們是利用用戶的id和地址表的user_id進行關聯的!
我想查詢出一個用戶的所有收貨地址!這個操作!!
我們先思考這個操作!首先,返回的一定是一個vo,然后我們的uservo里面要包含address的vo!也就是一個嵌套的操作!
我們先定義好這個vo,然后進行操作!
@GetMapping("/userList")@ApiOperation("查詢用戶信息")public List<UserVO> SelectQueryUserByIds(@ApiParam("用戶id集合") @RequestParam("ids") List<Long> ids) {return userService.queryUserAndAddressByIds(ids);}
我們先看一下controller,前端傳給我們一個id的集合,可能要查詢好幾個用戶的地址!
我們要返回一個list集合給前端!
我們來看一下service層!首先我們要根據前端傳過來的用戶id,查詢出所有的用戶,放到一個List里面!接下來我們把查詢出來的所有用戶的id放到一個集合里面userIds!
這里面引入一個Db的知識,只有在3.5.3 以上版本的mp才可以使用!Db.lamdbaQuery這樣可以避免循環依賴,Db可以不用注入service就直接使用!
我們利用Db,查詢出所有用戶id的地址放入到list集合里面!然后把地址的po轉成vo!
然后這里面有一個操作!就是我們要寫一個addressMap,因為我們需要將相同用戶的地址放入到一個集合里面!我們用stream流進行操作!我們最終返回的Map就是<用戶id,用戶地址集合>這樣的一個map!
然后我們最終還是要返回一個UserVO的一個集合,然后我們進行一個循環,讓user的po轉成vo后放入用戶的地址在userVO中,最后返回這個list即可!這個操作就做完了!!
我們還得講一下分頁查詢問題!
分頁查詢mp是需要封裝一個MybatisConfig的!
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;}
}
封裝好了后,我們提出一個需求!我們首先需要返回一個這樣數據結構的東西!
{"total": 100006,"pages": 50003,"list": [{"id": 1685100878975279298,"username": "user_9****","info": {"age": 24,"intro": "英文老師","gender": "female"},"status": "正常","balance": 2000}]
}
我們首先需要封裝一個pageQuery和PageDTO這兩個東西!
package com.itheima.mp.domain.query;import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import lombok.Data;@Data
@ApiModel(description = "分頁查詢條件實體")
public class PageQuery {private Integer pageNo = 1;private Integer pageSize = 5;private String sortBy;private Boolean isAsc = true;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);}
}
PageDTO
package com.itheima.mp.domain.dto;import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "分頁結果實體")
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);}
}
這兩個東西封裝完了,我們來看代碼!
紅色箭頭就是我們封裝好的Pagequery,這個UserQuery是繼承了Pagequery的,所以我們這個方法就是根據更新時間排序!
然后綠色箭頭就是我們的查詢條件,最后發揮一個VO即可,這個分頁查詢其實都是封裝好的!
最后分享一個插件我覺得非常好用!
這個可以直接使用生成代碼!第一個是連接數據庫,第二個是生成代碼!
你只要調整好文件,包...等等一切操作,他都可以幫你生成,但是邏輯你還是要自己寫的,哈哈哈哈哈哈!