Mybatis-Plus

l快速入门-简单介绍

微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合出复杂的大型应用。(两只小鸟)

  1. 引入依赖
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>2.3.1</version>
</dependency>
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
  <version>3.5.3.1</version>
</dependency>
  1. 定义Mapper

自定义的Mapper继承MybatisPlus提供的BaseMapper接口:

<User>为你实体类的实体,这样才能知道你操作的实体是什么

当添加完mybatisplus后,可以删去红色小鸟的sql语句内容,同样也能删去蓝色小鸟的实现类语句内容

直接调用Mapper后会出现很多固定的sql语句提供选择

以此实现0代码

只需简单的配置,即可快速进行单表CRUD操作,从而节省大量的时间

常见注解

MBP通过扫描实体类,并基于放射获取实体类信息作为数据库表信息

变量名转化形式

  • 类名驼峰转下划线作为表名
  • 默认把名为id的字段作为主键
  • 变量名驼峰转下划线作为表的字段名

MBP提供的注解常用于对应变量值无法探测到,而自行编写的形式

  • @ TableName: 用来指定表名
  • @Tableld :用来指定表中的主键字段信息
  • @TableFiled :用来指定表中的普通字段信息

常见配置

MBP的配置项继承了MB原生配置和一些自己特有的配置。

MBP使用基本流程:

  • 引入起步依赖
  • 自定义Mapper基础BaseMapper
  • 在实体类上添加注解声明表信息

核心功能-条件构造器

MBP支持各种复杂的where条件

Wrapper就是条件构造器

基于QueryWrapper的查询

@Test
void 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);

    @Test
    void testUpdateByQueryWrapper() {
        //1.要更新的数据
        User user = new User();
        user.setBalance(2000);
        //2.要更新的条件
        QueryWrapper<User> wrapper = new QueryWrapper<User>().eq("username","jack");
        //3.执行更新
        userMapper.update(user,wrapper);
    }

基于UpdateWrapper的更新

    @Test
    void testUpdateWrapper() {
        List<Long> ids = List.of(1L,2L,4L);
        UpdateWrapper<User> wrapper = new UpdateWrapper<User>()
                .setSql("balance = balance - 200")
                .in("id",ids);
        //3.执行更新
        userMapper.update(null,wrapper);
    }

为了避免硬编码,则使用lambda语法,

什么是lambda语法

先是在方法名上声明Lambda, 或者 .lambda()

接着其中将固定死的属性名转换成 表名::属性名的形式

自定义sql

我们可以利用MBP的Wrapper来构造复杂的where条件,然后自己定义sql语句剩下的部分

  1. 基于Wrapper构建where条件

  1. 在mapper方法参数中用Param声明wrapper变量名称,必须是ew

  1. 自定义sql,并使用wrapper条件

拼接语句实现sql

 @Test
    void testCustomSqlUpdate() {
        //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);
    }
//updateBalanceByIds为自定义方法,需要构造
public interface UserMapper extends BaseMapper<User> {
    List<User> queryUserByIds(@Param("id") List<Long> ids);
//注意加上ew、和注解
    void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);
}
    <update id="updateBalanceByIds">
        UPDATE user SET balance = balance - #{amount} ${ew.customSqlSegment}
    </update>
//在mapper层编写sql语句

Service接口

查询一个就用get ,查询多个就用list

新建接口,接口继承IService<实现实体类>

新建IMPL,继承ServiceImpl<实体mapper、实体类>继承接口

基于Restful风格实现下列接口

引入maven

<!--swagger-->
<dependency>
  <groupId>com.github.xiaoymin</groupId>
  <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
  <version>4.1.0</version>
</dependency>
<!--web-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置信息

knife4j:
  enable: true
  openapi:
    title: 用户管理接口文档
    description: "用户管理接口文档"
    email: zhanghuyi@itcast.cn
    concat: 虎哥
    url: https://www.itcast.cn
    version: v1.0.0
    group:
      default:
        group-name: default
        api-rule: package
        api-rule-resources:
          - com.itheima.mp.controller

以后简单的增删改查可以直接在service层实现,就是上面的1-4接口,可以简单实现

package com.itheima.mp.domain.controller;

import cn.hutool.core.bean.BeanUtil;
import com.itheima.mp.domain.dto.UserFormDTO;
import com.itheima.mp.domain.po.User;
import com.itheima.mp.domain.vo.UserVO;
import com.itheima.mp.service.IUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.annotations.Delete;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "用户管理接口")
@RequestMapping("/users")
@RestController
@RequiredArgsConstructor
public class UserController {

    private final IUserService userService;


    @ApiOperation("新增用户接口")
    @PostMapping
    public void saveUser(@RequestBody UserFormDTO userDTO){
        //1.把dto拷贝到po
        User user = BeanUtil.copyProperties(userDTO, User.class);
        //2.新增
        userService.save(user);
    }

    @ApiOperation("删除用户接口")
    @DeleteMapping("{id}")
    public void deleteUserById(@ApiParam("用户id") @PathVariable("id") Long id){
        userService.removeById(id);
    }

    @ApiOperation("根据id查询用户接口")
    @GetMapping("/{id}")
    public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
        //查询用户PO
        User user = userService.getById(id);
        //把PO拷贝到VO
        return BeanUtil.copyProperties(user, UserVO.class);
    }

    @ApiOperation("根据id批量查询用户id")
    @GetMapping
    public List<UserVO> queryUserById(@ApiParam("用户id集合") @RequestParam("ids") List<Long> ids){
        //查询用户PO
        List<User> users = userService.listByIds(ids);
        //把PO拷贝到VO
        return BeanUtil.copyToList(users, UserVO.class);
    }
}

但5则需要用到mapper层和service层

    @ApiOperation("扣减用户余额接口")
    @PutMapping("/{id}/deduction/{money}")
    public void deductMoneyById(
            @ApiParam("用户id") @PathVariable("id") Long id,
            @ApiParam("扣减的金额") @PathVariable("money") Integer money){
        userService.deductBalance(id, money);
    }
public interface IUserService extends IService<User> {


    void deductBalance(Long id, Integer money);
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    @Override
    public void deductBalance(Long id, Integer money) {
        //1.查询用户
        User user= getById(id);
        //2.校验用户状态
        if(user==null ||user.getStatus()==2){
            throw new RuntimeException("用户状态异常");
        }
        //3.校验余额是否充足
        if(user.getBalance() < money){
            throw new RuntimeException("用户余额不足");
        }
        //4.扣减余额   update tb_user set balance = balance - ?
        baseMapper.deductBalance(id,money);
    }
}
public interface UserMapper extends BaseMapper<User> {
    List<User> queryUserByIds(@Param("id") List<Long> ids);

    void updateBalanceByIds(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper,@Param("amount") int amount);
    
    @Update("update user set balance = balance - #{money} where id = #{id}")
    void deductBalance(@Param("id") Long id,@Param("money") Integer money);
}

然后启动springboot项目,打开swagger,测试文档http://localhost:8080/doc.html#/home

测试开发功能

IService的Lambda查询

lambda适用于帮助我们解决复杂的查询SQL语句的

    @Override
    @Transactional
    public void deductBalance(Long id, Integer money) {
        //1.查询用户
        User user= getById(id);
        //2.校验用户状态
        if(user==null ||user.getStatus()==2){
            throw new RuntimeException("用户状态异常");
        }
        //3.校验余额是否充足
        if(user.getBalance() < money){
            throw new RuntimeException("用户余额不足");
        }
        //4.扣减余额   update tb_user set balance = balance - ?
        int remainBalance = user.getBalance() - money;
        lambdaUpdate()
                .set(User::getBalance,remainBalance)
                .set(remainBalance ==0,User::getStatus,2)
                .eq(User::getId,id)
                .update();
    }
    @Override
    public List<User> queryUsers(String name, Integer status, Integer minBalance, Integer maxBalance) {
        return lambdaQuery()
                .like(name != null , User::getUsername , name)
                .eq(status != null , User::getStatus , status)
                .gt(minBalance != null , User::getBalance , minBalance)
                .lt(maxBalance != null , User::getBalance , maxBalance)
                .list() ;
    }

IService批量新增(批处理)

在配置文件中插入

代码不改,插入配置就能大大减少时长

代码生成(一次生成某表对应的四层框架)

一般流程:

插件

静态工具

DB静态工具,使用情况:service相互调用的情况下使用DB静态工具,既方便也避免了循环依赖

1、

    @ApiModelProperty("用户收获地址")
    private List<AddressVO> addresses;
    @ApiOperation("根据id查询用户接口")
    @GetMapping("{id}")
    public UserVO queryUserById(@ApiParam("用户id") @PathVariable("id") Long id){
        return userService.queryUserAndAddressById(id);
    }
    UserVO queryUserAndAddressById(Long id);
    @Override
    public UserVO queryUserAndAddressById(Long id) {
        //1,查询用户
        User user = getById(id);
        if(user == null ||user.getStatus()==2){
            throw new RuntimeException("用户状态异常");
        }
        //2。查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class)
                .eq(Address::getUserId, id).list();
        //3.封装VO
        //转user的po为vo
        UserVO userVO = BeanUtil.copyProperties(user, UserVO.class);
        //转地址vo
        if(CollUtil.isNotEmpty(addresses)){
            userVO.setAddresses(BeanUtil.copyToList(addresses, AddressVO.class));
        }
        return userVO;
    }

2、

    @Override
    public List<UserVO> queryUserAndAddressByIds(List<Long> ids) {
        //1.查询用户
        List<User> users = listByIds(ids);
        if(CollUtil.isEmpty(users)){
            return  Collections.emptyList();
        }
        //2.查询地址
        //获取用户id集合
        List<Long> userIds = users.stream().map(User::getId).collect(Collectors.toList());
        //根据用户id查询地址
        List<Address> addresses = Db.lambdaQuery(Address.class).in(Address::getUserId, userIds).list();
        //转换地址vo
        List<AddressVO> addressVOList = BeanUtil.copyToList(addresses, AddressVO.class);
        //用户地址集合分组处理,相同用户的放入同一个集合组中
        Map<Long,List<AddressVO>> addressMap = new HashMap<>(0);
        if(CollUtil.isNotEmpty(addressVOList)){
         addressMap = addressVOList.stream().collect(Collectors.groupingBy(AddressVO::getUserId));
        }

        //转换VO返回
        List<UserVO> list = new ArrayList<>(users.size());
        for(User user : users){
            //转换user的po为vo
            UserVO vo = BeanUtil.copyProperties(user, UserVO.class);
            list.add(vo);
            //转换地址vo
            vo.setAddresses(addressMap.get(user.getId()));
        }
        return list;
    }

逻辑删除

    @Test
    void testLongDelete(){
        //1.删除
        addressService.removeById(59L);
        //2.查询
        Address address = addressService.getById(59L);
        System.out.println("address = " + address);
    }

枚举处理器

枚举类型要和数据库中的类型相互转化

mbp的处理器:

为实现转化,加上注解

配置mbp,让注解生效

po、vo修改

userservice修改

JSON处理器

package com.itheima.mp.domain.po;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor(staticName = "of")
public class UserInfo {
    private Integer age;
    private String intro;
    private String gender;
}
    @ApiModelProperty("详细信息")
    private UserInfo info;




    /**
     * 详细信息
     */
    @TableField(typeHandler = JacksonTypeHandler.class)
    private UserInfo info;
@Test
    void testInsert() {
        User user = new User();
        user.setId(5L);
        user.setUsername("Lucy");
        user.setPassword("123");
        user.setPhone("18688990011");
        user.setBalance(200);
//        user.setInfo("{\"age\": 24, \"intro\": \"英文老师\", \"gender\": \"female\"}");
        user.setInfo(UserInfo.of(24,"英文老师","female"));
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.insert(user);
    }

插件功能-分页插件基本用法

分页插件

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();
        //1.创建分页插件
        PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        paginationInnerInterceptor.setMaxLimit(1000L);
        //2.添加分页插件
        interceptor.addInnerInterceptor(paginationInnerInterceptor);
        return interceptor;

    }
}
    @Test
    void testPageQuery(){
        int pageNo = 1, pageSize = 2;
        // 1.准备分页条件
        Page<User> page = new Page<>(pageNo, pageSize);
        page.addOrder(OrderItem.asc("balance"));
        // 2.分页查询
        Page<User> p = userService.page(page);
        // 3.解析
        long total = p.getTotal(); // 总记录数
        System.out.println("total = " + total);
        long pages = p.getPages(); // 总页数
        System.out.println("pages = " + pages);
        List<User> users = p.getRecords(); // 当前页的记录
        users.forEach(System.out::println);
    }

通用分页实体

创建pageDTO

package com.itheima.mp.domain.dto;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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
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

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Integer pageNo;
    @ApiModelProperty("页码")
    private Integer pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    private Boolean isAsc;
}

userquery继承pagequery

在controller层

    @ApiOperation("根据条件分页查询用户接口")
    @PutMapping("/page")
    public PageDTO<UserVO> queryUsersPage(UserQuery query){
        return userService.queryUsersPage(query);
    }

service实现类

    @Override
    public PageDTO<UserVO> queryUsersPage(UserQuery query) {
        String name = query.getName();
        Integer status = query.getStatus();
        // 1.构建分页条件
        Page<User> page = Page.of(query.getPageNo(), query.getPageSize());
        if(StrUtil.isNotBlank(query.getSortBy())){
            // 不为空
            page.addOrder(new OrderItem(query.getSortBy(), query.getIsAsc()));
        } else {
            // 为空,按默认更新时间排序
            page.addOrder(new OrderItem("update_time", false));
        }

        // 2.分页查询
        Page<User> p = lambdaQuery()
                .like(name != null, User::getUsername, name)
                .eq(status != null, User::getStatus, status)
                .page(page);

        // 3.封装VO结果
        PageDTO<UserVO> dto = new PageDTO<>();  // 修改为 PageDTO<UserVO>
        // 总条数
        dto.setTotal(p.getTotal());
        // 总页数
        dto.setPages(p.getPages());
        // 当前页数据
        List<User> records = p.getRecords();
        if(CollUtil.isEmpty(records)){
            dto.setList(Collections.emptyList());
            return dto;
        }
        // 拷贝 user 到 VO
        dto.setList(BeanUtil.copyToList(records, UserVO.class));
        // 4.返回
        return dto;
    }

通用分页实体与MP转换

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 io.swagger.annotations.ApiModelProperty;
import lombok.Data;

@Data
@ApiModel(description = "分页查询实体")
public class PageQuery {
    @ApiModelProperty("页码")
    private Integer pageNo;
    @ApiModelProperty("页码")
    private Integer pageSize;
    @ApiModelProperty("排序字段")
    private String sortBy;
    @ApiModelProperty("是否升序")
    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);
    }
}
package com.itheima.mp.domain.dto;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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
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);
    }
}

  • 5
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值