背景
配置代码生成器生成出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
泛型
public abstract class BaseController<S extends IService<M>, M> {
@Autowired
protected S service;
}
接着提取出公共的增删改查Mapping
public abstract class BaseController<S extends IService<M>, M extends BaseModel> {
@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后,接口文档的请求参数不能友好的定制化设置,如下图
参数因为是泛型,不好定制化处理,必须重写BaseController的方法,然后重新再方法中添加swaggerUI注解
2、如果需要扩展参数进行crud,必须重写父类BaseController中的方法,并且如果是扩展类型(DTO,VO)需要进行一次转换,不过这种转换的效率应该可以忽略不计
// 项目中的代码片段
@Log(title = "字典类型新增")
@Override
protected <DTO> Result baseAdd(@RequestBody DTO m) {
BaseDictTypeModel param = MapperUtil.convert(m, BaseDictTypeModel.class);
return Result.successBoolean(service.insertByParam(param));
}
重写注意要添加@RequestBody注解
总结
实际开发过程中并没有想象中的那么简化代码,但是提供了一个规则,使代码编写思路变的清晰,也减少了单表业务的代码量