简单介绍
后端的程序员往往苦于写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> {
}
运行效果如下