第4章 在线接口文档 & 代码模板改造
4.1 在线接口文档
4.1.1 概述
什么是 Swagger ? 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
**为什么用 Swagger **? 能实时生成的RESTful 风格的接口文档。
Swagger 官网:https://swagger.io/
**什么是 knife4j **? RuoYi的Swagger前端UI的增强解决方案。
knife4j 的作用?界面比Swagger更友好,还有离线文档,接口排序,安全控制,在线调试,文档清晰,注解增强,容易上手的优点。
knife4j 集成方式参考链接:
https://doc.ruoyi.vip/ruoyi-vue/document/cjjc.html#集成knife4j实现swagger文档增强
4.1.2 项目集成 Swagger 分几步?
一般用于测试,后续都会用 knife4j 增强UI。
1、添加依赖到ruoyi-admin\pom.xml
:
xml
<!-- swagger3-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.6.2</version>
</dependency>
<!-- ruoyi-springboot2 / swagger knife4j 配置 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
2、核心配置类 com.zzyl.web.core.config.SwaggerConfig
java
package com.zzyl.web.core.config;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.zzyl.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
/**
* Swagger2的接口配置
*
* @author peterpang
*/
@Configuration
public class SwaggerConfig
{
/** 系统基础配置 */
@Autowired
private RuoYiConfig ruoyiConfig;
/** 是否开启swagger */
@Value("${swagger.enabled}")
private boolean enabled;
/** 设置请求的统一前缀 */
@Value("${swagger.pathMapping}")
private String pathMapping;
/**
* 创建API
*/
@Bean
public Docket createRestApi()
{
return new Docket(DocumentationType.OAS_30)
// 是否启用Swagger
.enable(enabled)
// 用来创建该API的基本信息,展示在文档的页面中(自定义展示的信息)
.apiInfo(apiInfo())
// 设置哪些接口暴露给Swagger展示
.select()
// 扫描所有有注解的api,用这种方式更灵活
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// 扫描指定包中的swagger注解
// .apis(RequestHandlerSelectors.basePackage("com.zzyl.project.tool.swagger"))
// 扫描所有 .apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
/* 设置安全模式,swagger可以设置访问token */
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.pathMapping(pathMapping);
}
/**
* 安全模式,这里指定token通过Authorization头请求头传递
*/
private List<SecurityScheme> securitySchemes()
{
List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>();
apiKeyList.add(new ApiKey("Authorization", "Authorization", In.HEADER.toValue()));
return apiKeyList;
}
/**
* 安全上下文
*/
private List<SecurityContext> securityContexts()
{
List<SecurityContext> securityContexts = new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.operationSelector(o -> o.requestMappingPattern().matches("/.*"))
.build());
return securityContexts;
}
/**
* 默认的安全上引用
*/
private List<SecurityReference> defaultAuth()
{
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences = new ArrayList<>();
securityReferences.add(new SecurityReference("Authorization", authorizationScopes));
return securityReferences;
}
/**
* 添加摘要信息
*/
private ApiInfo apiInfo()
{
// 用ApiInfoBuilder进行定制
return new ApiInfoBuilder()
// 设置标题
.title("标题:中州养老_接口文档")
// 描述
.description("描述:用于管理中州养老的后端接口")
// 作者信息
.contact(new Contact(ruoyiConfig.getName(), null, null))
// 版本
.version("版本号:" + ruoyiConfig.getVersion())
.build();
}
}
含摘要信息:接口文档标题、描述、作者信息、版本。
3、访问 Swagger UI
方式一:在【若依管理系统】的【系统工具】的【系统接口】中访问。
方式二:访问服务地址+/Swagger-ui/index.html --- > http://localhost:8080/swagger-ui/index.html
4.1.3 项目集成 knife4j 分几步?
1、添加依赖到ruoyi-common\pom.xml
:
xml
<!-- ruoyi-springboot2 / swagger knife4j 配置 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
2、修改前端代码的ry-ui\views\tool\swagger\index.vue
跳转地址:src: process.env.VUE_APP_BASE_API + "/doc.html"
。
vue
<template>
<i-frame v-model:src="url"></i-frame>
</template>
<script setup>
import iFrame from '@/components/iFrame'
const url = ref(import.meta.env.VITE_APP_BASE_API + "/doc.html")//修改位置
</script>
提示:引用
knife4j-spring-boot-starter
依赖,项目中的swagger
依赖可以删除。
4.1.4 Swagger 注解
Swagger 注解能控制接口文档的生成内容。
常见注解如下:
注解 | 说明 |
---|---|
@Api | 用在控制层类上,描述Controller类的作用 |
@ApiOperation | 用在方法上,说明方法的用途、作用 |
@ApiParam(常用) | 用在参数上,描述单个形参的含义,适用于简单场景 |
@ApiImplicitParam(不常用) | 用在Controller类中的方法上方,描述单个形参的含义,适用于参数复杂或者需要详细描述参数的场景 |
@ApiModel | 用在实体类上,描述请求体或响应体的实体类的含义 |
@ApiModelProperty | 用在实体类的属性上,用来描述实体类中属性的含义 |
AI协助快速完成注解的编写:
Prompt
给我的代码添加上Swagger注解说明,要求每个参数都要添加说明,请使用下面几个注解:@Api、@ApiOperation、@ApiParam,每个注解只需要使用value属性描述作用即可,不要添加其他属性。
示例代码如下:
1、@Api、@ApiOperation、@ApiParam说明一类接口、接口方法、简单参数。
java
package com.zzyl.nursing.controller;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zzyl.common.annotation.Log;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.enums.BusinessType;
import com.zzyl.nursing.domain.NursingLevel;
import com.zzyl.nursing.service.INursingLevelService;
import com.zzyl.common.utils.poi.ExcelUtil;
import com.zzyl.common.core.page.TableDataInfo;
/**
* 护理等级Controller
*
* @author peterpang
* @date 2025-08-02
*/
@RestController
@RequestMapping("/nursing/level")
@Api("护理等级管理")
public class NursingLevelController extends BaseController
{
@Autowired
private INursingLevelService nursingLevelService;
/**
* 查询护理等级列表
*/
@PreAuthorize("@ss.hasPermi('nursing:level:list')")
@GetMapping("/list")
@ApiOperation("查询护理等级列表")
public TableDataInfo<List<NursingLevel>> list(NursingLevel nursingLevel)
{
startPage();
List<NursingLevel> list = nursingLevelService.selectNursingLevelList(nursingLevel);
return getDataTable(list);
}
/**
* 导出护理等级列表
*/
@PreAuthorize("@ss.hasPermi('nursing:level:export')")
@Log(title = "护理等级", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出护理等级列表")
public void export(
@ApiParam("Http响应对象") HttpServletResponse response,
@ApiParam("查询条件对象") NursingLevel nursingLevel)
{
List<NursingLevel> list = nursingLevelService.selectNursingLevelList(nursingLevel);
ExcelUtil<NursingLevel> util = new ExcelUtil<NursingLevel>(NursingLevel.class);
util.exportExcel(response, list, "护理等级数据");
}
/**
* 获取护理等级详细信息
*/
@PreAuthorize("@ss.hasPermi('nursing:level:query')")
@GetMapping(value = "/{id}")
@ApiOperation("获取护理等级详细信息")
public R<NursingLevel> getInfo(@ApiParam("要查询的护理等级ID") @PathVariable("id") Long id)
{
// return success(nursingLevelService.selectNursingLevelById(id));
return R.ok(nursingLevelService.selectNursingLevelById(id));
}
/**
* 新增护理等级
*/
@PreAuthorize("@ss.hasPermi('nursing:level:add')")
@Log(title = "护理等级", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增护理等级")
public AjaxResult add(@ApiParam("要新增的护理等级") @RequestBody NursingLevel nursingLevel)
{
return toAjax(nursingLevelService.insertNursingLevel(nursingLevel));
}
/**
* 修改护理等级
*/
@PreAuthorize("@ss.hasPermi('nursing:level:edit')")
@Log(title = "护理等级", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改护理等级")
public AjaxResult edit(@ApiParam("要修改的护理等级") @RequestBody NursingLevel nursingLevel)
{
return toAjax(nursingLevelService.updateNursingLevel(nursingLevel));
}
/**
* 删除护理等级
*/
@PreAuthorize("@ss.hasPermi('nursing:level:remove')")
@Log(title = "护理等级", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除护理等级")
public AjaxResult remove(@ApiParam("要删除的护理等级ID数组") @PathVariable Long[] ids)
{
return toAjax(nursingLevelService.deleteNursingLevelByIds(ids));
}
}
2、@ApiModel、@ApiModelProperty 说明实体类和实体类参数含义。
在实体类:
java
package com.zzyl.nursing.domain;
import java.math.BigDecimal;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zzyl.common.annotation.Excel;
import com.zzyl.common.core.domain.BaseEntity;
/**
* 护理等级对象 nursing_level
*
* @author peterpang
* @date 2025-08-02
*/
@ApiModel("护理等级对象")
public class NursingLevel extends BaseEntity
{
private static final long serialVersionUID = 1L;
/** 主键ID */
@ApiModelProperty("主键ID")
private Long id;
/** 等级名称 */
@Excel(name = "等级名称")
@ApiModelProperty("等级名称")
private String name;
/** 护理计划ID */
@Excel(name = "护理计划ID")
@ApiModelProperty("护理计划ID")
private Long planId;
/** 护理费用 */
@Excel(name = "护理费用")
@ApiModelProperty("护理费用")
private BigDecimal fee;
/** 状态(0:禁用,1:启用) */
@Excel(name = "状态", readConverterExp = "0=:禁用,1:启用")
@ApiModelProperty("状态(0:禁用,1:启用)")
private Integer status;
/** 等级说明 */
@Excel(name = "等级说明")
@ApiModelProperty("等级说明")
private String description;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setPlanId(Long planId)
{
this.planId = planId;
}
public Long getPlanId()
{
return planId;
}
public void setFee(BigDecimal fee)
{
this.fee = fee;
}
public BigDecimal getFee()
{
return fee;
}
public void setStatus(Integer status)
{
this.status = status;
}
public Integer getStatus()
{
return status;
}
public void setDescription(String description)
{
this.description = description;
}
public String getDescription()
{
return description;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("name", getName())
.append("planId", getPlanId())
.append("fee", getFee())
.append("status", getStatus())
.append("description", getDescription())
.append("remark", getRemark())
.append("createTime", getCreateTime())
.append("updateTime", getUpdateTime())
.append("createBy", getCreateBy())
.append("updateBy", getUpdateBy())
.toString();
}
}
在BaseEntity类下:
java
package com.zzyl.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* Entity基类
*
* @author peterpang
*/
@ApiModel("Entity基类")
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
@JsonIgnore
private String searchValue;
/** 创建者 */
@ApiModelProperty("创建者")
private String createBy;
/** 创建时间 */
@ApiModelProperty("创建时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新者 */
@ApiModelProperty("更新者")
private String updateBy;
/** 更新时间 */
@ApiModelProperty("更新时间")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 备注 */
@ApiModelProperty("备注")
private String remark;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ApiModelProperty("请求参数")
private Map<String, Object> params;
public String getSearchValue()
{
return searchValue;
}
public void setSearchValue(String searchValue)
{
this.searchValue = searchValue;
}
public String getCreateBy()
{
return createBy;
}
public void setCreateBy(String createBy)
{
this.createBy = createBy;
}
public Date getCreateTime()
{
return createTime;
}
public void setCreateTime(Date createTime)
{
this.createTime = createTime;
}
public String getUpdateBy()
{
return updateBy;
}
public void setUpdateBy(String updateBy)
{
this.updateBy = updateBy;
}
public Date getUpdateTime()
{
return updateTime;
}
public void setUpdateTime(Date updateTime)
{
this.updateTime = updateTime;
}
public String getRemark()
{
return remark;
}
public void setRemark(String remark)
{
this.remark = remark;
}
public Map<String, Object> getParams()
{
if (params == null)
{
params = new HashMap<>();
}
return params;
}
public void setParams(Map<String, Object> params)
{
this.params = params;
}
}
建议:不要将代码都放到若依框架的admin模块下,新建独立模块能增强项目的扩展性和维护性。
4.2 代码模板改造
4.2.1 若依自动代码生成原理
代码的自动生成基于什么?
Velocity模块引擎:一个基于Java的模板引擎,它可以通过特定的语法获取java对象的数据 , 填充到模板中,从而实现界面和java代码的分离
下面是后端的模板代码示例:
java
// Velocity模板开始,定义包名
package ${packageName}.domain;
// 循环导入所需的类库
#foreach ($import in $importList)
import ${import};
#end
// 导入其他必要的类库
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end
/**
* ${functionName}对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
// 定义Java类,继承自BaseEntity或TreeEntity
public class ${ClassName} extends ${Entity}
{
// 序列化版本ID
private static final long serialVersionUID = 1L;
// 循环定义类中的属性
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
// 添加属性的注释
/** $column.columnComment */
// 根据属性是否为列表类型,决定是否添加Excel注解
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)
// 如果属性名中有括号,则截取括号前的内容作为Excel注解的名字
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#else
// 如果是日期类型,添加JSON格式化注解
#elseif($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else
// 其他情况直接添加Excel注解
@Excel(name = "${comment}")
#end
#end
// 定义属性
private $column.javaType $column.javaField;
#end
#end
// 如果是子表,添加子表的集合属性
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;
#end
// 生成getter和setter方法
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
#set($AttrName=$column.javaField)
#else
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#end
// setter方法
public void set${AttrName}($column.javaType $column.javaField)
{
this.$column.javaField = $column.javaField;
}
// getter方法
public $column.javaType get${AttrName}()
{
return $column.javaField;
}
#end
#end
// 如果是子表,生成子表集合的getter和setter方法
#if($table.sub)
public List<${subClassName}> get${subClassName}List()
{
return ${subclassName}List;
}
public void set${subClassName}List(List<${subClassName}> ${subclassName}List)
{
this.${subclassName}List = ${subclassName}List;
}
#end
// 重写toString方法
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
#foreach ($column in $columns)
#if($column.javaField.length() > 2 && $column.javaField.substring(1,2).matches("[A-Z]"))
#set($AttrName=$column.javaField)
#else
#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})
#end
.append("${column.javaField}", get${AttrName}())
#end
#if($table.sub)
.append("${subclassName}List", get${subClassName}List())
#end
.toString();
}
}
- Velocity语法解释
${variable}
: 表示插入变量值。#foreach
和#end
: 循环结构,用于遍历列表。#if
和#end
: 条件判断结构。#set
: 设置变量。#elseif
: 条件分支。
- Java类结构说明
- 包声明: 定义类所在的包。
- 导入语句: 导入类所需要的类库。
- 类注释: 描述类的功能和作者信息。
- 类定义: 定义类并继承自
BaseEntity
或TreeEntity
。 - 属性定义: 根据模板参数定义类的属性。
- 属性注释: 对属性进行描述。
- Excel注解: 标记属性以方便导出Excel。
- getter和setter方法: 自动生成属性的访问方法。
- 子表集合: 如果是子表,则定义子表集合。
- 重写toString方法: 提供类实例的字符串表示形式。
下面是前端的模板代码示例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>velocity快速入门</title>
</head>
<body>
<h3>心怀梦想,坚持不懈,成功即在前方。${message}</h3>
</body>
</html>
下面是代码生成功能的核心代码:
代码位置:com.zzyl.generator.service.GenTableServiceImpl.generatorCode()
Java
/**
* 生成代码
* @param tableName 表名
* @param zip 压缩输出流
*/
private void generatorCode(String tableName, ZipOutputStream zip)
{
// 查询表信息
GenTable table = genTableMapper.selectGenTableByName(tableName);
// 设置主子表信息
setSubTable(table);
// 设置主键列信息
setPkColumn(table);
// 初始化Velocity引擎
VelocityInitializer.initVelocity();
// 准备Velocity上下文 获取该表的详细数据,并设置模板所需要的数据模型
VelocityContext context = VelocityUtils.prepareContext(table);
// 获取模板列表 读取resources/vm目录中的定义的模板文件
List<String> templates = VelocityUtils.getTemplateList(table.getTplCategory(), table.getTplWebType());
for (String template : templates)
{
// 渲染模板
StringWriter sw = new StringWriter();
Template tpl = Velocity.getTemplate(template, Constants.UTF8);
// 合并模板和数据模型
tpl.merge(context, sw);
try
{
// 将渲染结果添加到zip文件
zip.putNextEntry(new ZipEntry(VelocityUtils.getFileName(template, table)));
IOUtils.write(sw.toString(), zip, Constants.UTF8);
IOUtils.closeQuietly(sw);
zip.flush();
zip.closeEntry();
}
catch (IOException e)
{
// 记录日志
log.error("渲染模板失败,表名:" + table.getTableName(), e);
}
}
}
当前自动生成的代码有哪些问题:
- 为了生成接口文档,要手动添加 Swagger 相关注解。
- 实体类中含大量的
getter
、setter
、toString
方法。 - 希望使用更方便的MyBatis-Plus,而不是MyBatis。
- 日期时间从
Date
更换为更现代的LocalDateTime
。
改造目标:
- 支持自动添加Swagger的注解
- 支持Lombok
- 支持MyBatis-Plus
- 支持LocalDateTime
4.2.2 集成 Lombok
1、依赖导入
-
在父工程管理lombok的依赖
xml<!-- 版本统一管理 --> <properties> <lombok.version>1.18.22</lombok.version> </properties> <dependencyManagement> <dependencies> <!-- 其他依赖省略... --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> </dependencies> </dependencyManagement>
-
在若依的common模块中引入lombok依赖
xml<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
若发现没有反射信息,要手动刷新一下依赖,确保是最新依赖信息。
2、修改模板代码domain.java.vm
:
主要修改为:
- 添加lombok注解
- 删除生成属性的
get
/set
方法的代码 - 删除
toString
方法及其引用工具类的代码 - 删除主子表相关的
get
/set
方法的代码
最终代码如下:
java
package ${packageName}.domain;
#foreach ($import in $importList)
import ${import};
#end
import com.zzyl.common.annotation.DataScope;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* ${functionName}对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ${ClassName} extends ${Entity}
{
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
/** $column.columnComment */
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else
@Excel(name = "${comment}")
#end
#end
private $column.javaType $column.javaField;
#end
#end
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;
#end
}
3、代码预览并拷贝到项目中测试
无论是代码预览还是代码拷贝,都可以在【若依管理系统】的【系统工具】的【代码生成】中点击相应项的【预览】按钮进去页面中操作。
4.2.3 集成 MyBatis-Plus
将MyBatis-Plus集成到项目中很多细节,颇为麻烦,可以参考链接项目进行MP的完整改造:https://gitee.com/tellsea/ruoyi-vue-plus
1、添加依赖:
在父工程中的pom文件中添加MyBatis-Plus的依赖,如下:
XML
<properties>
<mybatis-plus-spring-boot.version>3.5.3.1</mybatis-plus-spring-boot.version>
</properties>
<!-- 依赖声明 -->
<dependencyManagement>
<dependencies>
<!-- MyBatis-Plus 增强CRUD -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-spring-boot.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
<version>${mybatis-plus-spring-boot.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
在若依的common模块中新增mybatis-plus的依赖
XML
<!--MyBatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-annotation</artifactId>
</dependency>
2、MP核心配置:
appliation.yml文件中新增MyBatisPlus配置,同时删除Mybatis相关的配置
YAML
# MyBatisPlus配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: com.zzyl.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 全局配置
global-config:
db-config:
id-type: auto #id生成策略为自增
configuration:
map-underscore-to-camel-case: true #字段与属性,自动转换为驼峰命名
新增核心配置类,删除MyBatisConfig
Java
package com.zzyl.framework.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* Mybatis Plus 配置
*
* @author ruoyi
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor() {
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor() {
return new BlockAttackInnerInterceptor();
}
}
3、修改生成的项目源码,为修改模板代码铺垫:
mapper层:

service层的接口和实现类:
service接口:

serviceImp实现类:

并且更改实现类中的CRUD接口为MP的接口方法:
java
package com.zzyl.nursing.service.impl;
import java.util.Arrays;
import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zzyl.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.NursingProjectMapper;
import com.zzyl.nursing.domain.NursingProject;
import com.zzyl.nursing.service.INursingProjectService;
/**
* 护理项目Service业务层处理
*
* @author ruoyi
* @date 2024-09-26
*/
@Service
public class NursingProjectServiceImpl extends ServiceImpl<NursingProjectMapper, NursingProject> implements INursingProjectService
{
@Autowired
private NursingProjectMapper nursingProjectMapper;
/**
* 查询护理项目
*
* @param id 护理项目主键
* @return 护理项目
*/
@Override
public NursingProject selectNursingProjectById(Long id)
{
// return nursingProjectMapper.selectNursingProjectById(id);
return getById(id);
}
/**
* 查询护理项目列表
*
* @param nursingProject 护理项目
* @return 护理项目
*/
@Override
public List<NursingProject> selectNursingProjectList(NursingProject nursingProject)
{
return nursingProjectMapper.selectNursingProjectList(nursingProject);
}
/**
* 新增护理项目
*
* @param nursingProject 护理项目
* @return 结果
*/
@Override
public int insertNursingProject(NursingProject nursingProject)
{
nursingProject.setCreateTime(DateUtils.getNowDate());
// return nursingProjectMapper.insertNursingProject(nursingProject);
return save(nursingProject) ? 1 : 0;
}
/**
* 修改护理项目
*
* @param nursingProject 护理项目
* @return 结果
*/
@Override
public int updateNursingProject(NursingProject nursingProject)
{
nursingProject.setUpdateTime(DateUtils.getNowDate());
// return nursingProjectMapper.updateNursingProject(nursingProject);
return updateById(nursingProject) ? 1 : 0;
}
/**
* 批量删除护理项目
*
* @param ids 需要删除的护理项目主键
* @return 结果
*/
@Override
public int deleteNursingProjectByIds(Long[] ids)
{
// return nursingProjectMapper.deleteNursingProjectByIds(ids);
return removeByIds(Arrays.asList(ids)) ? 1 : 0;
}
/**
* 删除护理项目信息
*
* @param id 护理项目主键
* @return 结果
*/
@Override
public int deleteNursingProjectById(Long id)
{
// return nursingProjectMapper.deleteNursingProjectById(id);
return removeById(id) ? 1 : 0;
}
}
4、若依的common模块 的 BaseEntity
类要添加如下注解,@TableField(exist = false)
表示,表示该字段不存在于数据库表中:
java
/**
* Entity基类
*
* @author ruoyi
*/
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
@JsonIgnore
@TableField(exist = false)
private String searchValue;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@TableField(exist = false)
private Map<String, Object> params;
}
5、改造模板代码:
在目前的模板文件中,咱们需要修改的模板共有3个,分别是:
- mapper.java.vm 继承
BaseMapper
- service.java.vm 继承
IService<T>
- serviceImpl.java.vm 继承
ServiceImpl<XxxMapper, T>
常见方法的使用(单表的增删改查)
mapper.java.vm:

service.java.vm:

serviceImpl.java.vm:

并且在代码中使用MP的getById
、save
、updateById
、removeByIds
、removeById
:
java
package ${packageName}.service.impl;
import java.util.List;
#foreach ($column in $columns)
#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
import com.zzyl.common.utils.DateUtils;
#break
#end
#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.zzyl.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Arrays;
/**
* ${functionName}Service业务层处理
*
* @author ${author}
* @date ${datetime}
*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service
{
@Autowired
private ${ClassName}Mapper ${className}Mapper;
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return ${functionName}
*/
@Override
public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
{
##return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
return getById(${pkColumn.javaField});
}
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
* @return ${functionName}
*/
@Override
public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
{
return ${className}Mapper.select${ClassName}List(${className});
}
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int insert${ClassName}(${ClassName} ${className})
{
#foreach ($column in $columns)
#if($column.javaField == 'createTime')
${className}.setCreateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)
int rows = ${className}Mapper.insert${ClassName}(${className});
insert${subClassName}(${className});
return rows;
#else
##return ${className}Mapper.insert${ClassName}(${className});
return save(${className}) ? 1 : 0;
#end
}
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int update${ClassName}(${ClassName} ${className})
{
#foreach ($column in $columns)
#if($column.javaField == 'updateTime')
${className}.setUpdateTime(DateUtils.getNowDate());
#end
#end
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
insert${subClassName}(${className});
#end
## return ${className}Mapper.update${ClassName}(${className});
return updateById(${className}) ? 1 : 0;
}
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的${functionName}主键
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
#end
## return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);
return removeByIds(Arrays.asList(${pkColumn.javaField}s)) ? 1 : 0;
}
/**
* 删除${functionName}信息
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
#end
## return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
return removeById(${pkColumn.javaField}) ? 1 : 0;
}
#if($table.sub)
/**
* 新增${subTable.functionName}信息
*
* @param ${className} ${functionName}对象
*/
public void insert${subClassName}(${ClassName} ${className})
{
List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();
if (StringUtils.isNotNull(${subclassName}List))
{
List<${subClassName}> list = new ArrayList<${subClassName}>();
for (${subClassName} ${subclassName} : ${subclassName}List)
{
${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
list.add(${subclassName});
}
if (list.size() > 0)
{
${className}Mapper.batch${subClassName}(list);
}
}
}
#end
}
注意:要检查BaseEntity类中的字段有没有加上
@TableField(exist = false)
表示,表示该字段不存在于数据库表中
6、代码预览并测试代码:
改造完成后,重新查看若依生成的代码:


接下来,先用代码生成功能重新生成一次三层架构的代码,替换到项目中,测试通过后再接着往下操作。
7、MP字段自动填充:
官网链接:https://baomidou.com/guides/auto-fill-field/
在MybatisPlus中通过两步可以实现这个功能:
在实体类中,使用@TableField
注解,来标明哪些字段是需要自动填充的,并且需要指定填充策略
Java
package com.zzyl.common.core.domain;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.Swagger.annotations.ApiModel;
import io.Swagger.annotations.ApiModelProperty;
/**
* Entity基类
*
* @author ruoyi
*/
@ApiModel("Entity基类")
public class BaseEntity implements Serializable
{
private static final long serialVersionUID = 1L;
/** 搜索值 */
@JsonIgnore
@TableField(exist = false)
private String searchValue;
/** 创建者 */
@ApiModelProperty(value = "创建者")
@TableField(fill = FieldFill.INSERT)
private String createBy;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "创建时间")
@TableField(fill = FieldFill.INSERT)
private Date createTime;
/** 更新者 */
@ApiModelProperty(value = "更新者")
@TableField(fill = FieldFill.UPDATE)
private String updateBy;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value = "更新时间")
@TableField(fill = FieldFill.UPDATE)
private Date updateTime;
/** 备注 */
@ApiModelProperty(value = "备注")
private String remark;
/** 请求参数 */
@JsonInclude(JsonInclude.Include.NON_EMPTY)
@ApiModelProperty(value = "请求参数")
@TableField(exist = false)
private Map<String, Object> params;
}
在若依的framework模块中新增MyMetaObjectHandler
来处理字段自动填充

详细的代码如下:
Java
package com.zzyl.framework.interceptor;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, DateUtils.getNowDate());
this.strictInsertFill(metaObject, "createBy", String.class, String.valueOf(getLoginUser()));
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", Date.class, DateUtils.getNowDate());
this.strictUpdateFill(metaObject, "updateBy", String.class, String.valueOf(getLoginUser()));
}
/**
* 获取当前登录人的ID
*
* @return 登录人ID
*/
public Long getLoginUser() {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null) {
return loginUser.getUserId();
}
return 1L;
}
}
检查字段填充是否有效,在数据库中看保存的创建时间和更新时间是否是当前时区的时间。
如果不对,换一种更新方式this.setFieldValByName()
:
java
package com.zzyl.framework.interceptor;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.utils.SecurityUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
// 自动填充创建人
this.strictInsertFill(metaObject, "createBy", String.class, String.valueOf(getLoginUser()));
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", new Date(), metaObject);
this.setFieldValByName("updateBy", String.valueOf(getLoginUser()), metaObject);
// this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
// this.strictUpdateFill(metaObject, "updateBy", String.class, String.valueOf(getLoginUser()));
}
public Long getLoginUser() {
LoginUser loginUser = SecurityUtils.getLoginUser();
if (loginUser != null) {
return loginUser.getUserId();
}
return 1L;
}
}
8.更改模板代码在 serviceImp
中,去掉update和inseet的时间填充。
下面是 serviceImpl.java.vm
模板代码:
Java
package ${packageName}.service.impl;
import java.util.List;
#foreach ($column in $columns)
#if($column.javaField == 'createTime' || $column.javaField == 'updateTime')
import com.zzyl.common.utils.DateUtils;
#break
#end
#end
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#if($table.sub)
import java.util.ArrayList;
import com.zzyl.common.utils.StringUtils;
import org.springframework.transaction.annotation.Transactional;
import ${packageName}.domain.${subClassName};
#end
import ${packageName}.mapper.${ClassName}Mapper;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import java.util.Arrays;
/**
* ${functionName}Service业务层处理
*
* @author ${author}
* @date ${datetime}
*/
@Service
public class ${ClassName}ServiceImpl extends ServiceImpl<${ClassName}Mapper, ${ClassName}> implements I${ClassName}Service
{
@Autowired
private ${ClassName}Mapper ${className}Mapper;
/**
* 查询${functionName}
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return ${functionName}
*/
@Override
public ${ClassName} select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
{
## return ${className}Mapper.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
return getById(${pkColumn.javaField});
}
/**
* 查询${functionName}列表
*
* @param ${className} ${functionName}
* @return ${functionName}
*/
@Override
public List<${ClassName}> select${ClassName}List(${ClassName} ${className})
{
return ${className}Mapper.select${ClassName}List(${className});
}
/**
* 新增${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int insert${ClassName}(${ClassName} ${className})
{
###foreach ($column in $columns)
###if($column.javaField == 'createTime')
## ${className}.setCreateTime(DateUtils.getNowDate());
###end
###end
#if($table.sub)
int rows = ${className}Mapper.insert${ClassName}(${className});
insert${subClassName}(${className});
return rows;
#else
## return ${className}Mapper.insert${ClassName}(${className});
return save(${className}) ? 1 : 0;
#end
}
/**
* 修改${functionName}
*
* @param ${className} ${functionName}
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int update${ClassName}(${ClassName} ${className})
{
###foreach ($column in $columns)
###if($column.javaField == 'updateTime')
## ${className}.setUpdateTime(DateUtils.getNowDate());
###end
###end
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${className}.get${pkColumn.capJavaField}());
insert${subClassName}(${className});
#end
## return ${className}Mapper.update${ClassName}(${className});
return updateById(${className}) ? 1 : 0;
}
/**
* 批量删除${functionName}
*
* @param ${pkColumn.javaField}s 需要删除的${functionName}主键
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}s(${pkColumn.javaField}s);
#end
## return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s);
return removeByIds(Arrays.asList(${pkColumn.javaField}s)) ? 1 : 0;
}
/**
* 删除${functionName}信息
*
* @param ${pkColumn.javaField} ${functionName}主键
* @return 结果
*/
#if($table.sub)
@Transactional
#end
@Override
public int delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaType} ${pkColumn.javaField})
{
#if($table.sub)
${className}Mapper.delete${subClassName}By${subTableFkClassName}(${pkColumn.javaField});
#end
## return ${className}Mapper.delete${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField});
return removeById(${pkColumn.javaField}) ? 1 : 0;
}
#if($table.sub)
/**
* 新增${subTable.functionName}信息
*
* @param ${className} ${functionName}对象
*/
public void insert${subClassName}(${ClassName} ${className})
{
List<${subClassName}> ${subclassName}List = ${className}.get${subClassName}List();
${pkColumn.javaType} ${pkColumn.javaField} = ${className}.get${pkColumn.capJavaField}();
if (StringUtils.isNotNull(${subclassName}List))
{
List<${subClassName}> list = new ArrayList<${subClassName}>();
for (${subClassName} ${subclassName} : ${subclassName}List)
{
${subclassName}.set${subTableFkClassName}(${pkColumn.javaField});
list.add(${subclassName});
}
if (list.size() > 0)
{
${className}Mapper.batch${subClassName}(list);
}
}
}
#end
}
下面是要去掉的代码:
java
###foreach ($column in $columns)
###if($column.javaField == 'createTime')
## ${className}.setCreateTime(DateUtils.getNowDate());
###end
###end
###foreach ($column in $columns)
###if($column.javaField == 'updateTime')
## ${className}.setUpdateTime(DateUtils.getNowDate());
###end
###end
4.2.4 集成 Swagger
需要修改2个模板文件:controller.java.vm 和domain.java.vm,先来修改controller.java.vm文件,具体的添加的内容如下:
在controller.java.vm中,在头添加如下代码
java
import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
其他的主要修改部位是添加 @Api
、@ApiOperation
、@ApiParam
以及修改 success
为R.ok
、TableDataInfo
改为泛型 TableDataInfo<List<${ClassName}>>
,下面是示例代码:
java
package ${packageName}.controller;
import com.zzyl.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.zzyl.common.annotation.Log;
import com.zzyl.common.core.controller.BaseController;
import com.zzyl.common.core.domain.AjaxResult;
import com.zzyl.common.enums.BusinessType;
import ${packageName}.domain.${ClassName};
import ${packageName}.service.I${ClassName}Service;
import com.zzyl.common.utils.poi.ExcelUtil;
#if($table.crud || $table.sub)
import com.zzyl.common.core.page.TableDataInfo;
#elseif($table.tree)
#end
/**
* ${functionName}Controller
*
* @author ${author}
* @date ${datetime}
*/
@RestController
@RequestMapping("/${moduleName}/${businessName}")
@Api(tags = "${functionName}相关接口")
public class ${ClassName}Controller extends BaseController
{
@Autowired
private I${ClassName}Service ${className}Service;
/**
* 查询${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:list')")
@GetMapping("/list")
@ApiOperation("查询${functionName}列表")
#if($table.crud || $table.sub)
public TableDataInfo<List<${ClassName}>> list(@ApiParam(value = "${functionName}查询条件") ${ClassName} ${className})
{
startPage();
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return getDataTable(list);
}
#elseif($table.tree)
public AjaxResult list(${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
return success(list);
}
#end
/**
* 导出${functionName}列表
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:export')")
@Log(title = "${functionName}", businessType = BusinessType.EXPORT)
@PostMapping("/export")
@ApiOperation("导出${functionName}列表")
public void export(
@ApiParam(value = "HTTP响应对象,用于输出导出文件") HttpServletResponse response,
@ApiParam(value = "${functionName}查询条件") ${ClassName} ${className})
{
List<${ClassName}> list = ${className}Service.select${ClassName}List(${className});
ExcelUtil<${ClassName}> util = new ExcelUtil<${ClassName}>(${ClassName}.class);
util.exportExcel(response, list, "${functionName}数据");
}
/**
* 获取${functionName}详细信息
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:query')")
@GetMapping(value = "/{${pkColumn.javaField}}")
@ApiOperation("获取${functionName}详细信息")
public R<${ClassName}> getInfo(@ApiParam(value = "${functionName}ID") @PathVariable("${pkColumn.javaField}") ${pkColumn.javaType} ${pkColumn.javaField})
{
return R.ok(${className}Service.select${ClassName}By${pkColumn.capJavaField}(${pkColumn.javaField}));
}
/**
* 新增${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:add')")
@Log(title = "${functionName}", businessType = BusinessType.INSERT)
@PostMapping
@ApiOperation("新增${functionName}")
public AjaxResult add(@ApiParam(value = "${functionName}信息", required = true) @RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.insert${ClassName}(${className}));
}
/**
* 修改${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:edit')")
@Log(title = "${functionName}", businessType = BusinessType.UPDATE)
@PutMapping
@ApiOperation("修改${functionName}")
public AjaxResult edit(@ApiParam(value = "${functionName}信息", required = true) @RequestBody ${ClassName} ${className})
{
return toAjax(${className}Service.update${ClassName}(${className}));
}
/**
* 删除${functionName}
*/
@PreAuthorize("@ss.hasPermi('${permissionPrefix}:remove')")
@Log(title = "${functionName}", businessType = BusinessType.DELETE)
@DeleteMapping("/{${pkColumn.javaField}s}")
@ApiOperation("删除${functionName}")
public AjaxResult remove(
@ApiParam(value = "${functionName}ID数组", required = true) @PathVariable ${pkColumn.javaType}[] ${pkColumn.javaField}s)
{
return toAjax(${className}Service.delete${ClassName}By${pkColumn.capJavaField}s(${pkColumn.javaField}s));
}
}
下面是TableDataInfo
改造为TableDataInfo<T>
代码:
java
package com.zzyl.common.core.page;
import java.io.Serializable;
import java.util.List;
/**
* 表格分页数据对象
*
* @author ruoyi
*/
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;
/**
* 表格数据对象
*/
public TableDataInfo()
{
}
/**
* 分页
*
* @param list 列表数据
* @param total 总记录数
*/
public TableDataInfo(List<T> list, long total)
{
this.rows = list;
this.total = total;
}
public long getTotal()
{
return total;
}
public void setTotal(long total)
{
this.total = total;
}
public List<T> getRows()
{
return rows;
}
public void setRows(List<T> rows)
{
this.rows = rows;
}
public int getCode()
{
return code;
}
public void setCode(int code)
{
this.code = code;
}
public String getMsg()
{
return msg;
}
public void setMsg(String msg)
{
this.msg = msg;
}
}
再来修改 domain.java.vm
文件,先在头加上如下代码:
java
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
再添加 @ApiModel
、 @ApiModelProperty
,下面是修改后的示例代码:
java
package ${packageName}.domain;
#foreach ($import in $importList)
import ${import};
#end
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import com.zzyl.common.annotation.DataScope;
import com.zzyl.common.annotation.Excel;
#if($table.crud || $table.sub)
import com.zzyl.common.core.domain.BaseEntity;
#elseif($table.tree)
import com.zzyl.common.core.domain.TreeEntity;
#end
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* ${functionName}对象 ${tableName}
*
* @author ${author}
* @date ${datetime}
*/
#if($table.crud || $table.sub)
#set($Entity="BaseEntity")
#elseif($table.tree)
#set($Entity="TreeEntity")
#end
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("${functionName}信息")
public class ${ClassName} extends ${Entity}
{
private static final long serialVersionUID = 1L;
#foreach ($column in $columns)
#if(!$table.isSuperColumn($column.javaField))
/** $column.columnComment */
#if($column.list)
#set($parentheseIndex=$column.columnComment.indexOf("("))
#if($parentheseIndex != -1)
#set($comment=$column.columnComment.substring(0, $parentheseIndex))
#else
#set($comment=$column.columnComment)
#end
#if($parentheseIndex != -1)
@Excel(name = "${comment}", readConverterExp = "$column.readConverterExp()")
#elseif($column.javaType == 'Date')
@JsonFormat(pattern = "yyyy-MM-dd")
@Excel(name = "${comment}", width = 30, dateFormat = "yyyy-MM-dd")
#else
@Excel(name = "${comment}")
#end
#end
@ApiModelProperty("${column.columnComment}")
private $column.javaType $column.javaField;
#end
#end
#if($table.sub)
/** $table.subTable.functionName信息 */
private List<${subClassName}> ${subclassName}List;
#end
}
我们来重启一下服务,看一下代码预览:

4.2.5 支持LocalDateTime类型
1、前端改为支持LocalDateTime的Java类型
打开前端项目,找到src/views/tool/gen/editTable.vue文件,在这个文件添加中一行代码,如下:

html
<el-option label="LocalDateTime" value="LocalDateTime" />
2、后端改为支持LocalDateTime的类型
打开VelocityUtils类,这里面有处理导入包的逻辑,找到getImportList方法,在其中添加对应的包导入如下代码:
java
// 如果字段类型在前端选择的是LocalDateTime,则导入对应的包
if (!column.isSuperColumn() && GenConstants.TYPE_LOCAL_DATE_TYPE.equals(column.getJavaType()))
{
importList.add("java.time.LocalDateTime");
// 导入这个是为了格式化日期
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
}
这里是导入后的示例代码:
Java
/**
* 根据列类型获取导入包
*
* @param genTable 业务表对象
* @return 返回需要导入的包列表
*/
public static HashSet<String> getImportList(GenTable genTable)
{
List<GenTableColumn> columns = genTable.getColumns();
GenTable subGenTable = genTable.getSubTable();
HashSet<String> importList = new HashSet<String>();
if (StringUtils.isNotNull(subGenTable))
{
importList.add("java.util.List");
}
for (GenTableColumn column : columns)
{
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType()))
{
importList.add("java.util.Date");
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
}
// 如果字段类型在前端选择的是LocalDateTime,则导入对应的包
if (!column.isSuperColumn() && GenConstants.TYPE_LOCAL_DATE_TYPE.equals(column.getJavaType()))
{
importList.add("java.time.LocalDateTime");
// 导入这个是为了格式化日期
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
}
else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType()))
{
importList.add("java.math.BigDecimal");
}
}
return importList;
}
并且新增代码中的 TYPE_LOCAL_DATE_TYPE 需要定义在 GenConstants
常量类中。
java
/** JDK8时间类型 */
public static final String TYPE_LOCAL_DATE_TYPE= "LocalDateTime";
3、修改模板代码
修改模块文件domain.java.vm文件,修改的内容如下:

java
#elseif($column.javaType == 'Date' || $column.javaType == 'LocalDateTime')
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
4、生成代码进行预览并测试,这是重要的一步。
4.3 自动生成代码的默认设置
自动生成代码有哪些默认设置:
- 默认作者
- 默认生成包路径
- 默认物理类型
- 默认Java类型
4.3.1 修改作者和生成包路径
若依默认生成的包路径和模块名都是system,效果如下:

而咱们项目中用到的包路径和模块名都是nursing,如果要将这里的默认值改为nursing,只需要修改配置文件即可:
修改zzyl-generator模块下的generator.yml配置文件,修改后的内容如下:
YAML
# 代码生成
gen:
# 作者
author: peterpeng
# 默认生成包路径 system 需改成自己的模块名称 如 system monitor tool
packageName: com.zzyl.nursing
# 自动去除表前缀,默认是false
autoRemovePre: false
# 表前缀(生成类名不会包含表前缀,多个用逗号分隔)
tablePrefix: sys_
# 是否允许生成文件覆盖到本地(自定义路径),默认不允许
allowOverwrite: false
4.3.2 修改物理类型对应的Java类型
若依默认生成的字段信息中很多数据库中的int
类型和tinyint
类型对应的Java类型都是Long
,如下图所示:

可以通过修改zzyl-generator模块下的GenUtils工具类中的代码来将其统一改为INTEGER类型,具体的修改位置为:
Java
/**
* 初始化列属性字段
*/
public static void initColumnField(GenTableColumn column, GenTable table)
{
String dataType = getDbType(column.getColumnType());
String columnName = column.getColumnName();
column.setTableId(table.getTableId());
column.setCreateBy(table.getCreateBy());
// 设置java字段名
column.setJavaField(StringUtils.toCamelCase(columnName));
// 设置默认类型
column.setJavaType(GenConstants.TYPE_STRING);
column.setQueryType(GenConstants.QUERY_EQ);
if (arraysContains(GenConstants.COLUMNTYPE_STR, dataType) || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType))
{
// 字符串长度超过500设置为文本域
Integer columnLength = getColumnLength(column.getColumnType());
String htmlType = columnLength >= 500 || arraysContains(GenConstants.COLUMNTYPE_TEXT, dataType) ? GenConstants.HTML_TEXTAREA : GenConstants.HTML_INPUT;
column.setHtmlType(htmlType);
}
else if (arraysContains(GenConstants.COLUMNTYPE_TIME, dataType))
{
// 将日期时间类型改为LocalDateTime(重点)
column.setJavaType(GenConstants.TYPE_LOCAL_DATE_TIME);
column.setHtmlType(GenConstants.HTML_DATETIME);
}
else if (arraysContains(GenConstants.COLUMNTYPE_NUMBER, dataType))
{
column.setHtmlType(GenConstants.HTML_INPUT);
// 如果是浮点型 统一用BigDecimal
String[] str = StringUtils.split(StringUtils.substringBetween(column.getColumnType(), "(", ")"), ",");
if (str != null && str.length == 2 && Integer.parseInt(str[1]) > 0)
{
column.setJavaType(GenConstants.TYPE_BIGDECIMAL);
}
// 如果是整形
else if (str != null && str.length == 1 && Integer.parseInt(str[0]) <= 10
|| GenConstants.MYSQL_TINYINT.equals(column.getColumnType()) || GenConstants.MYSQL_INT.equals(column.getColumnType()))//(重点)
{
column.setJavaType(GenConstants.TYPE_INTEGER);
}
// 长整形
else
{
column.setJavaType(GenConstants.TYPE_LONG);
}
}
......省略了后续代码
}
新增代码中的 LocalDateTime、 tinyint 和 int 类型需要定义在
GenConstants
常量类中。
Java
/** LocalDateTime时间类型 */
public static final String TYPE_LOCAL_DATE_TIME = "LocalDateTime";
/** MySql tinyint 类型 */
public static final String MYSQL_TINYINT = "tinyint";
/** MySql int 类型 */
public static final String MYSQL_INT = "int";
这样,在系统中导入表结构后,数据库中的tinyint
类型和int
类型对应的默认Java类型就都是Integer
类型啦。