MybatisPlus深入学习

今天深入的学习了一下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即可,这个分页查询其实都是封装好的!

最后分享一个插件我觉得非常好用!

这个可以直接使用生成代码!第一个是连接数据库,第二个是生成代码!

你只要调整好文件,包...等等一切操作,他都可以帮你生成,但是逻辑你还是要自己写的,哈哈哈哈哈哈!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值