Controller封装基础的CRUD,简化单表业务

背景

工作过部分软件开发公司,大部分软件的开发步骤都是框架搭建后,配置代码生成器生成出controller、service、mapper,虽然大部分开源框架service与mapper都生成好了,但是controller中还遗留冗余代码,我的想法是能不能把基本的单表CRUD抽出来,提供一个公共的Controller控制器。
利用java语言的特性可以实现,也会存在一些问题。

实现思路

  • 创建父控制器BaseController
  • 一个Model(表实体Model)对应一个service,service作为泛型为BaseController的属性
  • 通过注入的方式注入service属性(表不可能重复,因此service不存在重复注入Spring容器)
  • 提取增删改查Mapping

service接口需要提供通用增删改查接口,对持久层框架没有限制

具体步骤

我是用的是mybatis-plus框架,service泛型需要继承自IService接口,这个接口是MybatisPlus提供的公共service层接口,基本的增删改查方法接口已经提供好了,需要传递一个Model泛型

1
2
3
4
5
6
public abstract class BaseController<S extends IService<M>, M> {

@Autowired
protected S service;

}

接着提取出公共的增删改查Mapping

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
public abstract class BaseController<S extends IService<M>, M> {

@Autowired
protected S service;

@ResponseBody
@GetMapping("/baseQueryById/{id}")
@ApiOperation(value = "基础功能-通过ID查询单条记录", notes = "基础功能-通过ID查询单条记录", httpMethod = "GET", response = Result.class)
@ApiOperationSupport(order = 1)
protected Result baseQueryById(@PathVariable("id") Long id) {
if (ObjectUtil.isNull(id) || id <= 0L) {
return Result.fail("id不能为空!");
}
M m = service.getById(id);
return Result.success(m);
}

@ResponseBody
@PostMapping("/baseQueryByParam")
@ApiOperation(value = "基础功能-条件查询", httpMethod = "POST", response = Result.class)
@ApiOperationSupport(order = 2)
protected Result baseQueryByParam(@RequestBody(required = false) M param) {
if (ObjectUtil.isNull(param)) {
param = service.modelInstance();
}
List<M> list = service.listByParam(param);
return Result.success(list);
}

@ResponseBody
@PostMapping("/baseQueryPageByParam")
@ApiOperation(value = "基础功能-条件查询分页", httpMethod = "POST", response = Result.class)
@ApiOperationSupport(order = 3)
protected Result baseQueryPageByParam(@RequestBody(required = false) M param) {
if (ObjectUtil.isNull(param)) {
param = service.modelInstance();
}
IPage<M> page = PageUtil.pageBean(param);
return Result.success(service.listPageByParam(page, param));
}

@Log(title = "基础新增")
@ResponseBody
@PostMapping("/baseAdd")
@ApiOperation(value = "基础功能-新增", httpMethod = "POST", response = Result.class)
@ApiOperationSupport(order = 4)
protected <DTO> Result baseAdd(@RequestBody DTO m) {
M param = JSON.parseObject(JSON.toJSONString(m), service.getModelClass());
if (!service.save(param)) {
return Result.fail(ResultEnum.FAIL_INSERT);
}
return Result.success(ResultEnum.SUCCESS_INSERT);
}

@Log(title = "基础编辑")
@ResponseBody
@PutMapping("/baseEdit")
@ApiOperation(value = "基础功能-修改", httpMethod = "PUT", response = Result.class)
@ApiOperationSupport(order = 5)
protected <DTO> Result baseEdit(@RequestBody @Validated(value = {Update.class}) DTO m) {
M param = JSON.parseObject(JSON.toJSONString(m), service.getModelClass());
if (!service.updateById(param)) {
return Result.fail(ResultEnum.FAIL_UPDATE);
}
return Result.success(ResultEnum.SUCCESS_UPDATE);
}

@Log(title = "基础删除")
@ResponseBody
@DeleteMapping("/baseDeleteByIds/{ids}")
@ApiOperation(value = "基础功能-根据主键id删除(多个id根据,分隔)", httpMethod = "DELETE", response = Result.class)
@ApiOperationSupport(order = 6)
protected Result baseDeleteByIds(@PathVariable("ids") String ids) {
if (StrUtil.isEmpty(ids)) {
return Result.fail("删除条件id不能为空");
}
String[] idsArr = ids.split(StringPool.COMMA);
if (idsArr.length > 1000) {
return Result.fail("不能批量删除超过1000个数据");
}
List<Long> idList = StringUtils.splitToList(ids, Long::valueOf);
if (service.removeByIds(idList)) {
return Result.success(ResultEnum.SUCCESS_DELETE);
}
return Result.success(ResultEnum.FAIL_DELETE);
}

}

其中Result为公共的返回实体,Log是我在项目中封装的日志注解,还用到了SwaggerUi接口文档框架

遇到的问题

1、引入swaggerUi后,接口文档的请求参数不能友好的定制化设置,如下图

图片.png

参数因为是泛型,不好定制化处理,必须重写BaseController的方法,然后重新再方法中添加swaggerUI注解

2、如果需要扩展参数进行crud,必须重写父类BaseController中的方法,并且如果是扩展类型(DTO,VO)需要进行一次转换,不过这种转换的效率应该可以忽略不计

1
2
3
4
5
6
7
// 项目中的代码片段
@Log(title = "字典类型新增")
@Override
protected <DTO> Result baseAdd(@RequestBody DTO m) {
BaseDictTypeModel param = MapperUtil.convert(m, BaseDictTypeModel.class);
return Result.successBoolean(service.insertByParam(param));
}

重写注意要添加@RequestBody注解

总结

实际开发过程中并没有想象中的那么简化代码,但是提供了一个规则,使代码编写思路变的清晰,也减少了单表业务的代码量

项目的源码地址:
gitee源码地址
github源码地址


Controller封装基础的CRUD,简化单表业务
http://www.codersand.fun/2023/04/21/course/Controller封装基础的CRUD/
作者
吴昊
发布于
2023年4月21日
许可协议