今天深入的学习了一下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 {
@Autowired
private UserMapper userMapper;
@Test
void 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);
}
@Test
void testSelectById() {
User user = userMapper.selectById(5L);
System.out.println("user = " + user);
}
@Test
void testQueryByIds() {
List<User> users = userMapper.selectBatchIds(List.of(1L, 2L, 3L, 4L));
users.forEach(System.out::println);
}
@Test
void testUpdateById() {
User user = new User();
user.setId(5L);
user.setBalance(20000);
userMapper.updateById(user);
}
@Test
void testDeleteUser() {
userMapper.deleteById(5L);
}
@Test
void 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);
}
@Test
void testUpdate() {
//1.要更新的数据
User user = new User();
user.setBalance(2000);
// 2. 构造更新条件
QueryWrapper<User> userQueryWrapper = new QueryWrapper<User>()
.eq("username", "jack");
userMapper.update(user, userQueryWrapper);
}
@Test
void 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);
}
@Test
void 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);
}
@Test
void 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
@Transactional
public 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
@Transactional
public 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 {
@Bean
public 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即可,这个分页查询其实都是封装好的!
最后分享一个插件我觉得非常好用!
这个可以直接使用生成代码!第一个是连接数据库,第二个是生成代码!
你只要调整好文件,包...等等一切操作,他都可以帮你生成,但是逻辑你还是要自己写的,哈哈哈哈哈哈!