1. 为什么自己开发插件
目前市面上基于Mybatis或MybatisPlus的代码生成器或插件有很多,本人前几年也开发了一款:基于SpringBoot微服务代码自动生成插件。之前的开发的这款插件底层使用的持久层框架是通用Mapper,不是现在主流的MyBatisPlus,而且只支持mysql数据库,加上多年没有维护更新了,已经不适用于当前企业的开发需求了。
本人对市面上主流的基于MyBatis或MyBatisPlus插件使用后发现,这些插件生成的所有代码都只能对单表进行增删改查操作,如果需要同时对主从表进行增删改查就必须自己手写代码,而现实开发中,同时对主从表进行增删改查的需求是很常见的,这不得不使得开发人员需要花费大量的时间来编写同时对主从表进行增删改查的代码,严重的影响了开发进度和降低了开发效率。因此本人决定自己开发一款基于SpringBoot和MyBatisPlus的代码生成器,要实现三个目标:
- 不仅要实现单表的增删改查,还要支持同时对主从表的增删改查。
- 开发人员只需要把表结构设计好,通过代码生成器生成代码后无需做任何修改就可以让前端进行接口联调。
- 至少减少开发人员90%的代码量。
目前插件已经开发完成并在公司多个项目中使用了,以上三个目标也得实现,下面就给介绍下这款代码生成插件的具体功能和使用方式。
2. 插件功能说明
- 自动生成实体类:并配置好主从表的关系(如果有主从表)
- 自动生成Mapper接口和对应的xml文件
- 自动生成IBaseService接口文件:包含多个增删改查的方法以及多个供开发者根据需求进行扩展的方法
- 自动生成业务层类:实现了IBaseService接口
- 自动生成控制器类:包含了多个接口供前端调用对表进行增删改查操作
- 自动生成SpringBoot工程启动类
- 自动生成pom.xml文件:文件中包含了项目运行需要使用的依赖
3. 插件使用演示
- 选择项目工程的任意文件夹右键
- 点击插件名称后弹框如下图
- 选择表(可选)
选择数据库厂商,填写数据库配置信息,选择要生成代码的表(这一步可以省略,如果不选择表则表示对数据库所有的表进行代码生成),选择好表之后直接点关闭按钮即可。
- 配置主从表关系
重要:如果表与表之间有主从关联,需要按下图方式进行配置,否则无法同时进行主从表的增删改查操作。
可以配置多组主从表关系,点击添加就会将选择好的信息移动到文本输入框显示,如果选择错了,可以手动编辑文本输入框,删除选错的主从表关系,多个主从表关系之间使用的是英文分号分割,配置好之后点确定按钮关闭配置主从表对话框即可。
- 继续填写剩下的信息
最后点击确定按钮即可根据配置自动生成代码并保存到指定的目录下。
4. 生成代码概述
4.1 表关系说明
先说明下上一步选择的四张表的关系:
task:任务表
task_user:任务用户表
task_stage:任务阶段表
task_scene:任务场景表
一个任务可以下发给多个用户,因此task表和task_user表关系是:一对多
一个任务有多个阶段,因此task表和task_stage表关系是:一对多
一个阶段有多个场景,因此task_stage表和task_scene表关系是:一对多
总结:一个任务分为多个阶段,每个阶段有多个场景,每个任务可以分给多个任务去完成
4.2 实体类包
选择的四个表对应的四个实体类
4.4.1 Task类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("task")
public class Task implements Serializable{
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
@ApiModelProperty(value = "任务名称")
@TableField("name")
private String name;
// 省略多个字段.....
// 下面两个成员变量就是根据配置的表关系额外生成的
@ApiModelProperty(value = "与子表:task_stage 关系:一对多")
@TableField(exist = false)
@ChildTable(childClass = TaskStage.class, foreignKey = "task_id")
private List<TaskStage> taskStageList;
@ApiModelProperty(value = "与子表:task_user 关系:一对多")
@TableField(exist = false)
@ChildTable(childClass = TaskUser.class, foreignKey = "task_id")
private List<TaskUser> taskUserList;
}
4.4.2 TaskStage类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("task_stage")
public class TaskStage implements Serializable{
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
@ApiModelProperty(value = "阶段名称")
@TableField("name")
private String name;
// 省略多个字段.....
// 下面成员变量就是根据配置的表关系额外生成的
@ApiModelProperty(value = "与子表:task_scene 关系:一对多")
@TableField(exist = false)
@ChildTable(childClass = TaskScene.class, foreignKey = "task_stage_id")
private List<TaskScene> taskSceneList;
}
4.4.3 TaskUser类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("task_user")
public class TaskUser implements Serializable{
@ApiModelProperty(value = "培训任务ID")
@TableField("task_id")
private String taskId;
@ApiModelProperty(value = "学员名称")
@TableField("user_name")
private String userName;
// 省略多个字段.....
}
4.4.4 TaskScene类
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("task_scene")
public class TaskScene implements Serializable{
@ApiModelProperty(value = "主键")
@TableId(value = "id", type = IdType.ASSIGN_ID)
private String id;
@ApiModelProperty(value = "场景名称")
@TableField("name")
private String name;
// 省略多个字段.....
@ApiModelProperty(value = "关联阶段表主键")
@TableField("task_stage_id")
private String taskStageId;
}
4.3 Mapper接口包
4.4 Service包
4.5 Controller包
4.6 其他
5. 接口使用演示
在每个控制器中都生成了如下几个接口,这里以Task
表对应的控制器来演示说明
@Api(tags = "task表前端控制器")
@RestController
@RequestMapping("/task")
public class TaskController {
@Autowired
private TaskService taskService;
@ApiOperation(value = "查询 task 表所有记录-不分页")
@PostMapping(value = "/findAll" )
public R findAll(){
return R.ok(taskService.findByCondition(null));
}
@ApiOperation(value = "根据条件分页查询 task 表记录")
@PostMapping(value = "/findByCondition" )
public R findByCondition(@RequestBody(required = false) TaskDTO taskDTO){
return R.ok(taskService.findByCondition(taskDTO));
}
@ApiOperation(value = "根据id删除 task 表记录")
@ApiImplicitParam(name = "id", value = "主键ID", required = true)
@GetMapping(value = "/deleteById" )
public R deleteById(@RequestParam(value = "id") String id){
return R.ok(taskService.deleteById(id));
}
@ApiOperation(value = "新增或修改 task 表记录")
@PostMapping(value="/saveOfUpdate")
public R saveOfUpdate(@RequestBody Task task){
return R.ok(taskService.saveOfUpdate(task));
}
@ApiOperation(value = "根据id查询 task 表详情")
@ApiImplicitParam(name = "id", value = "主键ID", required = true)
@GetMapping("/findById")
public R findById(@RequestParam(value = "id") String id){
return R.ok(taskService.findById(id));
}
}
5.1 新增或更新任务
5.1.1 对应控制器方法
@ApiOperation(value = "新增或修改 task 表记录")
@PostMapping(value="/saveOfUpdate")
public R saveOfUpdate(@RequestBody Task task){
return R.ok(taskService.saveOfUpdate(task));
}
5.1.2 无id则代表新增
执行请求之后,会在任务表task中添加一条记录,在阶段表task_stage中添加两条记录,每个阶段在场景表task_scene中有两条记录,在task_user表中有两条记录。
5.1.3 有id则代表更新
执行请求之后,会更新主表和与之关联的所有从表的信息。
5.2 删除任务
5.2.1 对应控制器的方法
@ApiOperation(value = "根据id删除 task 表记录")
@ApiImplicitParam(name = "id", value = "主键ID", required = true)
@GetMapping(value = "/deleteById" )
public R deleteById(@RequestParam(value = "id") String id){
return R.ok(taskService.deleteById(id));
}
5.2.2 通过PostMan调用接口
执行请求后会根据id删除task表对应的记录,同时也会删除从表task_stage,task_user与之关联的记录,task_stage表关联的从表task_scene中相关联的记录也会删除。
5.3 查询任务详情
5.3.1 对应控制器的方法
@ApiOperation(value = "根据id查询 task 表详情")
@ApiImplicitParam(name = "id", value = "主键ID", required = true)
@GetMapping("/findById")
public R findById(@RequestParam(value = "id") String id){
return R.ok(taskService.findById(id));
}
5.3.2 通过PostMan调用接口
执行请求后会根据id查询task表的对应记录,与之关联的所有的从表记录也会查询出来,结果如下图:
5.4 查询所有任务
5.4.1 对应控制器的方法
@ApiOperation(value = "查询 task 表所有记录-不分页")
@PostMapping(value = "/findAll" )
public R findAll(){
return R.ok(taskService.findByCondition(null));
}
5.4.2 通过PostMan调用接口
该接口是不带条件不分页的查询表中的所有记录。
5.5 分页查询任务列表
5.5.1 对应控制器的方法
@ApiOperation(value = "根据条件分页查询 task 表记录")
@PostMapping(value = "/findByCondition" )
public R findByCondition(@RequestBody(required = false) TaskDTO taskDTO){
return R.ok(taskService.findByCondition(taskDTO));
}
5.5.2 参数TaskDTO类概述
- 每个表都会生成一个对应DTO类,用来封装分页参数和查询条件的,默认生成的DTO类如下所示:
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class TaskDTO implements Serializable{
/**
* 分页查询页号
*/
private Integer pageNo = 1;
/**
* 分页查询页大小
*/
private Integer pageSize = 20;
}
5.5.3 通过PostMan调用接口
执行请求之后就能分页查询表记录了,返回结果如下图:
5.6 分页带条件查询任务列表
@ApiOperation(value = "根据条件分页查询 task 表记录")
@PostMapping(value = "/findByCondition" )
public R findByCondition(@RequestBody(required = false) TaskDTO taskDTO){
return R.ok(taskService.findByCondition(taskDTO));
}
与上一个接口是同一个接口,只需要在dto类中配置下条件信息即可,比如查询列表时要根据任务名称过滤列表数据,只需要在dto中添加一个字段并使用自定义注解@Condition配置下就可以,如下图所示:
如果需要有更多的过滤条件,继续添加字段即可,不需要修改自动生成的代码就实现了带条件分页查询列表了。
5.7 接口使用小结
以上演示的接口都是插件自动生成就有的,不管是单表和有主从关系的表都可以使用。不需要任何修改就能让前端调用了。如果执行增删改查时需要对请求参数或查询结果做特殊处理,比如在新增数据时需要对请求参数进行校验,校验不通过则会直接响应给前端,可以重写:boolean beforeSaveOfUpdate(T t)
方法。还有很多方法可以让使用者根据业务需求决定是否需要重写。如果没有特殊需求的,完全不需要写代码就可以实现增删改查接口的开发。