Mybatis-plus自动生成CRUD接口,可以实现通过实体类任意属性查询,CRUD神器

简单介绍

后端的程序员往往苦于写CRUD的编写,但是在出现Mybatis-Plus之后,大大简化了编写sql完成CRUD操作的繁琐步骤。当然自动生成代码也是快速编写代码的重要的一步,对此由于之前有写过相关的博客,这边就不多赘述了,可以参考
mybatis-plus自动生成库中所有表的entity、dao、service和controller的代码

言归正传,面对重复的业务,还是需要编写许多重复的代码去完成CRUD操作,那有没有什么方法可以减少大量重复的代码编写呢?

请接往下看

自动根据实体类的属性生成QueryWrapper对象

使用过Mybatis-Plus的都知道QueryWrapper是其中代替sql完成查询的重要对象,那么有没有办法去自动根据实体类的属性去自动完成查询呢,可以去探寻的IService接口,由于在其中找到可以通过实体类查询的方法,但是其中存在通过Wrapper查询的方法

在这里插入图片描述

那么我们可以转变下思路,通过实体类属性生成Wrapper然后再去完成查询,我们可以借助java的泛型和反射编写以下代码实现通过实体类的非空属性生成Wrapper的代码,以下代码借助了Hutool完成驼峰转下划线,jar包如下

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.16</version>
</dependency>
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import cn.hutool.core.util.StrUtil;
import java.lang.reflect.Field;

public class QueryWrapperUtil {

    /**
     * 根据实体类生成QueryWrapper
     * @param entity 实体类
     * @return QueryWrapper
     * @param <E> 实体类泛型
     * @throws IllegalAccessException
     */
    public static <E> QueryWrapper<E> getQueryWrapper(E entity) throws IllegalAccessException {
        Class<?> clazz = entity.getClass();
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // 使private属性可以被访问
            field.setAccessible(true);
            // 判断属性值非空就添加到Wrapper的查中(这边由于实体类都是使用Mybatis-Plus自动生成的,存在serialVersionUID序列化的属性,需要把它排除出去)
            if (field.get(entity) != null && !"serialVersionUID".equals(field.getName())) {
                queryWrapper.eq(StrUtil.toUnderlineCase(field.getName()), field.get(entity));
            }
            // 在使用后使其保持private
            field.setAccessible(false);
        }
        return queryWrapper;
    }

}

好了,自动生成Wrapper的工具类编写完成了,接下来就是自动接口的生成了

自动生成CRUD接口

这边写提一下文档接口使用了knife4j,jar包maven地址如下

<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
    <version>4.4.0</version>
</dependency>

接下来就是编写BaseController接口,其他Controller可以通过继承该接口来完成自动实现CRUD,实现代码如下

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.grain.common.entity.PageInfo;
import com.grain.common.entity.Response;
import com.grain.common.entity.TableDataInfo;
import com.grain.common.utils.QueryWrapperUtil;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


public class BaseController<S extends IService<E>, E> {

    @Autowired
    protected S baseService;

    @Operation(summary = "根据实体类中非空的属性分页查询")
    @GetMapping("/list")
    public Response<TableDataInfo<E>> listByInfo(PageInfo pageInfo, E entity) {
        return list(pageInfo, entity, false);
    }

    /**
     * 格局分页信息和实体类中非空信息进行查询
     * @param pageInfo 分页信息
     * @param entity 实体类
     * @param haveDeleteFlag 是否有删除标记
     * @return 分页查询结果
     */
    public Response<TableDataInfo<E>> list(PageInfo pageInfo, E entity, boolean haveDeleteFlag) {
        QueryWrapper<E> queryWrapper;
        try {
            queryWrapper = QueryWrapperUtil.getQueryWrapper(entity);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        return list(pageInfo, queryWrapper, true, "id", haveDeleteFlag, "delete_flag");
    }

    /**
     * 默认按照id倒序排序,且不查询没有被删除的数据
     * @param pageInfo 分页信息
     * @param queryWrapper 查询条件
     * @return 分页查询结果
     */
    public Response<TableDataInfo<E>> list(PageInfo pageInfo, QueryWrapper<E> queryWrapper) {
        return list(pageInfo, queryWrapper, true, "id", false, "delete_flag");
    }

    /**
     * 使用默认delete_flag字段判断记录是否删除进行查询操作
     * @param pageInfo 分页信息
     * @param haveDeleteFlag 是否有删除标记
     * @return 分页查询结果
     */
    public Response<TableDataInfo<E>> list(PageInfo pageInfo, boolean haveDeleteFlag) {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        return list(pageInfo, queryWrapper, true, "id", haveDeleteFlag, "delete_flag");
    }

    /**
     * 当无分页参数时,默认查询所有数据,有分页参数时,分页查询数据
     * @param pageInfo 分页信息
     * @param queryWrapper 查询条件
     * @param isByIdDesc 是否按ID倒序排序
     * @param idStr ID字段名称
     * @param haveDeleteFlag 是否有删除标记
     * @param deleteFlagName 删除标记字段名称
     * @return 分页查询结果
     */
    public Response<TableDataInfo<E>> list(PageInfo pageInfo,
                                           QueryWrapper<E> queryWrapper,
                                           boolean isByIdDesc,
                                           String idStr,
                                           boolean haveDeleteFlag,
                                           String deleteFlagName) {
        if (isByIdDesc) {
            queryWrapper.orderByDesc(idStr);
        }
        if (haveDeleteFlag) {
            queryWrapper.eq(deleteFlagName, 0);
        }
        if (pageInfo.getPage() == null || pageInfo.getPageSize() == null) {
            return Response.successWithData(TableDataInfo.build(baseService.list(queryWrapper)));
        }
        Page<E> page = new Page<>(pageInfo.getPage(), pageInfo.getPageSize());
        return Response.successWithData(TableDataInfo.build(baseService.page(page, queryWrapper)));
    }

    @Operation(summary = "根据ID获取详细信息")
    @GetMapping("/{id}")
    public Response<E> get(@PathVariable(value = "id") Long id) {
        return Response.successWithData(baseService.getById(id));
    }

    @Operation(summary = "新增记录")
    @PostMapping("/add")
    public Response add(@RequestBody E entity) {
        boolean saveFlag = baseService.save(entity);
        return saveFlag ? Response.successWithMessage("添加记录成功!") : Response.errorWithMsg("添加记录失败!");
    }

    @Operation(summary = "更新记录")
    @PutMapping("/update")
    public Response update(@RequestBody E entity) {
        boolean updateFlag = baseService.updateById(entity);
        return updateFlag ? Response.successWithMessage("更新记录成功!") : Response.errorWithMsg("更新记录失败!");
    }

    /**
     * 逻辑删除
     * @param id 主键ID
     * @return 删除结果
     */
    public Response logicDelete(Long id) {
        E entity = baseService.getById(id);
        if (entity == null) {
            return Response.errorWithMsg("id不存在!");
        }
        UpdateWrapper<E> updateWrapper = new UpdateWrapper<E>()
                .eq("id", id)
                .set("delete_flag", 1);
        boolean updateFlag = baseService.update(updateWrapper);
        if (updateFlag) {
            return Response.successWithMessage("删除成功!");
        }
        return Response.errorWithMsg("删除失败!");
    }

    // 由于大部分情况下都会使用逻辑删除而不会直接删除库中的数据,可以参考logicDelete()方法
//    @Operation(summary = "删除记录")
//    @GetMapping("/delete/{id}")
//    public Response<String> delete(@PathVariable(value = "id") Long id) {
//        boolean deleteFlag = baseService.removeById(id);
//        return deleteFlag ? Response.successWithMessage("删除记录成功!") : Response.errorWithMsg("删除记录失败!");
//    }

}

以下是一些其中用到的类的实现,Response为通用返回,可以参考实际实际使用的,这边就不提供了

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageInfo {

    /**
     * 分页大小
     */
    private Integer pageSize;

    /**
     * 当前页数
     */
    private Integer page;

}

TableDataInfo类来自Ruoyi

import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.core.metadata.IPage;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

/**
 * 表格分页数据对象
 *
 * @author Lion Li
 */

@Data
@NoArgsConstructor
public class TableDataInfo<T> implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * 总记录数
     */
    private long total;

    /**
     * 列表数据
     */
    private List<T> rows;

    /**
     * 消息状态码
     */
    private int code;

    /**
     * 消息内容
     */
    private String msg;

    /**
     * 分页
     *
     * @param list  列表数据
     * @param total 总记录数
     */
    public TableDataInfo(List<T> list, long total) {
        this.rows = list;
        this.total = total;
    }

    public static <T> TableDataInfo<T> build(IPage<T> page) {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(page.getRecords());
        rspData.setTotal(page.getTotal());
        return rspData;
    }

    public static <T> TableDataInfo<T> build(List<T> list) {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        rspData.setRows(list);
        rspData.setTotal(list.size());
        return rspData;
    }

    public static <T> TableDataInfo<T> build() {
        TableDataInfo<T> rspData = new TableDataInfo<>();
        rspData.setCode(HttpStatus.HTTP_OK);
        rspData.setMsg("查询成功");
        return rspData;
    }

}
使用及效果

通过编写代码测试下knife4j的体验下效果,编写如下sql表

CREATE TABLE `user` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `login_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '登录名',
  `password` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '密码',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='用户表';

借助Mybatis-Plus的代码生成器生成实体类,mapper和service代码,具体代码如下

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;

import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * <p>
 * 用户表
 * </p>
 *
 * @author Grain
 * @since 2024-05-27
 */
@Getter
@Setter
@Accessors(chain = true)
@TableName("user")
@Schema(name = "User对象", description = "用户表")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @Schema(description = "登录名")
    private String loginName;

    @Schema(description = "密码")
    @TableField("`password`")
    private String password;
}
import com.grain.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * <p>
 * 用户表 Mapper 接口
 * </p>
 *
 * @author Grain
 * @since 2024-05-27
 */
public interface UserMapper extends BaseMapper<User> {

}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.grain.mapper.UserMapper">

</mapper>
import com.grain.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 * 用户表 服务类
 * </p>
 *
 * @author Grain
 * @since 2024-05-27
 */
public interface UserService extends IService<User> {

}
import com.grain.entity.User;
import com.grain.mapper.UserMapper;
import com.grain.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Grain
 * @since 2024-05-27
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}

准备完成了,接下来就是编写Controller了,代码如下

import com.grain.entity.User;
import com.grain.service.UserService;
import com.grain.common.controller.BaseController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test/user")
public class UserTestController extends BaseController<UserService, User> {
    
}

运行效果如下
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值