一、前言
在阿里巴巴Java开发手册中对工程结构进行如下分层
1.1 应用分层
- 开放接口层:可直接封装 Service 方法暴露成 RPC 接口;通过 Web 封装成 http 接口;进行网关安全控制、流量控制等
- 终端显示层:各个端的模板渲染并执行显示的层。当前主要是 velocity 渲染,JS 渲染,JSP 渲染,移动端展示等。
- Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
- Service 层:相对具体的业务逻辑服务层。
- Manager 层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对 Service 层通用能力的下沉,如缓存方案、中间件通用处理;
3) 与 DAO 层交互,对多个 DAO 的组合复用。 - DAO 层:数据访问层,与底层 MySQL、Oracle、Hbase 等进行数据交互。
- 外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。
1.2 分层领域模型规约
- DO(Data Object):与数据库表结构一一对应,通过 DAO 层向上传输数据源对象。
- DTO(Data Transfer Object):数据传输对象,Service 或 Manager 向外传输的对象。
- BO(Business Object):业务对象。由 Service 层输出的封装业务逻辑的对象。
- VO(View Object):显示层对象,通常是 Web 向模板渲染引擎层传输的对象。
- Query:数据查询对象,各层接收上层的查询请求。注意超过 2 个参数的查询封装,禁止使用 Map 类来传输。
二、代码生成器
我们新建一个springboot项目,版本为2.7.15(一开始使用2.3.2版本发现存在兼容性问题),使用的mybatis-plus-generator的版本为3.5.3.2,使用velocity模板引擎
2.1 pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.15</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.21</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
<exclusions>
<exclusion>
<artifactId>jsqlparser</artifactId>
<groupId>com.github.jsqlparser</groupId>
</exclusion>
</exclusions>
</dependency>
<!--swagger相关依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<artifactId>error_prone_annotations</artifactId>
<groupId>com.google.errorprone</groupId>
</exclusion>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<!--swagger页面-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 application.yml文件
如果项目中有引入swagger且在启动的时候报错注意配置请求路径的匹配策略spring.mvc.pathmatch.matchingstrategy=ant_path_matcher
server:
port: 8088
spring:
application:
name: demo
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: admin
password: root123
mvc:
pathmatch:
matching-strategy: ant_path_matcher
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
typeAliasesPackage: com.example.generator.pojo
logging:
level:
com.example.generator.mapper: debug
2.3 配置文件和工具类
mybatis-plus配置类,因为我在项目中使用了mybatis的分页插件,不配置无法正常分页
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.github.pagehelper.PageInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
/**
* @Author
* @Date 2023/10/9 17:57
* @Description pageHelper分页插件&&mybatisPlus分页插件
**/
@Configuration
public class MybatisPlusConfiguration {
/**
* 配置分页插件
* 包括PageHelper和MyBatis-Plus的分页插件
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 添加PaginationInnerInterceptor内部拦截器,用于支持MyBatis-Plus的分页功能
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
}
/**
* 配置MyBatis-Plus的分页插件
*/
@Bean
public PaginationInnerInterceptor paginationInterceptor() {
return new PaginationInnerInterceptor();
}
/**
* 配置PageHelper的分页插件
*/
@Bean
PageInterceptor pageInterceptor() {
PageInterceptor pageInterceptor = new PageInterceptor();
// 配置属性helperDialect为"mysql",表示使用MySQL数据库
Properties properties = new Properties();
properties.setProperty("helperDialect", "mysql");
pageInterceptor.setProperties(properties);
return pageInterceptor;
}
}
数据库类型枚举类
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
/**
* @Author
* @Date 2023/8/30 15:32
* @Description
**/
public enum MyDbColumnType implements IColumnType{
TINYINT("tinyint", (String)null);
private final String type;
private final String pkg;
MyDbColumnType(final String type, final String pkg) {
this.type = type;
this.pkg = pkg;
}
@Override
public String getType() {
return this.type;
}
@Override
public String getPkg() {
return this.pkg;
}
}
swagger配置类
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
/**
* @Author
* @Date 2022/9/21 20:11
* @Description 如果启动失败配置 spring.mvc.pathmatch.matching-strategy=ant_path_matcher
**/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
Contact contact = new Contact("XXX","","XXXX@qq.com");
ApiInfo apiInfo=new ApiInfo(
"XXX系统接口",
"包含系统内所有可用接口说明",
"v1.0",
"",
contact,
"",
"",
new ArrayList<>()
);
return this.createRestApi(apiInfo);
}
public Docket createRestApi(ApiInfo apiInfo) {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo)
.select()
//这里采用包含注解的方式来确定要显示的接口(建议使用这种)
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
}
工具类,主要用来复制对象属性
import java.util.function.Supplier;
/**
* @Author
* @Date 2023/9/1 10:54
* @Description
**/
public class BeanUtils {
public BeanUtils() {
}
public static <S, T> T copyProperties(S source, Supplier<T> targetSupplier) {
if (null != source && null != targetSupplier) {
T t = targetSupplier.get();
org.springframework.beans.BeanUtils.copyProperties(source, t);
return t;
} else {
return null;
}
}
}
2.4 模板文件
在resources文件夹下新建templates文件夹,将我们自定义的模板文件放在templates文件夹下
2.4.1 do.java.vm
package $!{package.Parent}.pojo;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableField;
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
/**
*
* $!{table.comment} ($!{table.name})数据源对象
*
* @author ${author}
* @since ${date}
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@TableName("${schemaName}${table.name}")
public class ${entity} implements Serializable {
#set($serialVersionUID = $table.name.getBytes("UTF-8").hashCode() + "L")
private static final long serialVersionUID = $serialVersionUID;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
#if(${field.keyFlag})
#if(${field.keyIdentityFlag})
@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
#else
@TableField("${field.annotationColumnName}")
#end
private ${field.propertyType} ${field.propertyName};
#end
}
2.4.2 bo.java.vm
package $!{package.Parent}.bo;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
/**
*
* $!{table.comment} ($!{table.name})业务对象
*
* @author ${author}
* @since ${date}
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ${entity}BO implements Serializable {
#set($serialVersionUID = $table.name.getBytes("UTF-8").hashCode() + "L")
private static final long serialVersionUID = $serialVersionUID;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
private ${field.propertyType} ${field.propertyName};
#end
}
2.4.3 dto.java.vm
package $!{package.Parent}.dto;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.experimental.Accessors;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
/**
*
* $!{table.comment} ($!{table.name})数据传输对象
*
* @author ${author}
* @since ${date}
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class ${entity}DTO implements Serializable {
#set($serialVersionUID = $table.name.getBytes("UTF-8").hashCode() + "L")
private static final long serialVersionUID = $serialVersionUID;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
private ${field.propertyType} ${field.propertyName};
#end
}
2.4.4 vo.java.vm
package $!{package.Parent}.vo;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.experimental.Accessors;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
/**
*
* $!{table.comment} ($!{table.name})显示层对象
*
* @author ${author}
* @since ${date}
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "$!{entity}VO", description = "$!{table.comment}显示层对象")
public class ${entity}VO implements Serializable {
#set($serialVersionUID = $table.name.getBytes("UTF-8").hashCode() + "L")
private static final long serialVersionUID = $serialVersionUID;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
@ApiModelProperty(name = "$!{field.propertyName}", value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#end
}
2.4.5 query.java.vm
package $!{package.Parent}.query;
import lombok.Data;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.experimental.Accessors;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
/**
*
* $!{table.comment} ($!{table.name})数据查询对象
*
* @author ${author}
* @since ${date}
*/
@Data
@Builder
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "$!{entity}Query", description = "$!{table.comment}数据查询对象")
public class ${entity}Query implements Serializable {
#set($serialVersionUID = $table.name.getBytes("UTF-8").hashCode() + "L")
private static final long serialVersionUID = $serialVersionUID;
#foreach($field in ${table.fields})
#if("$!field.comment" != "")
/**
* ${field.comment}
*/
#end
@ApiModelProperty(name = "$!{field.propertyName}", value = "${field.comment}")
private ${field.propertyType} ${field.propertyName};
#end
}
2.4.6 mapper.xml.vm
<?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="${package.Mapper}.${table.mapperName}">
#set($VOResultMap = ${entity}+"VO"+"Map")
<!-- 开启二级缓存 -->
<cache></cache>
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="${package.Parent}.pojo.${entity}">
#foreach($field in ${table.fields})
#if(${field.keyFlag})##生成主键排在第一位
<id column="${field.name}" property="${field.propertyName}"/>
#end
#end
#foreach($field in ${table.commonFields})##生成公共字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
#foreach($field in ${table.fields})
#if(!${field.keyFlag})##生成普通字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
#end
</resultMap>
<!-- 显示层对象映射结果 -->
<resultMap id="${VOResultMap}" type="${package.Parent}.vo.${entity}VO">
#foreach($field in ${table.fields})
#if(${field.keyFlag})##生成主键排在第一位
<id column="${field.name}" property="${field.propertyName}"/>
#end
#end
#foreach($field in ${table.commonFields})##生成公共字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
#foreach($field in ${table.fields})
#if(!${field.keyFlag})##生成普通字段
<result column="${field.name}" property="${field.propertyName}"/>
#end
#end
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
#foreach($field in ${table.commonFields})
${field.columnName},
#end
${table.fieldNames}
</sql>
<!--查询指定行数据-->
<select id="findAll" resultMap="$!{VOResultMap}">
select
<include refid="Base_Column_List"/>
from $!{table.name}
<where>
#foreach($field in $table.fields)
<if test="${field.propertyName} != null#if($field.propertyType.equals("String")) and ${field.propertyName} != ''#end">
and ${field.name} = #{${field.propertyName}}
</if>
#end
</where>
</select>
</mapper>
2.4.7 mapper.java.vm
package ${package.Mapper};
import ${package.Parent}.pojo.${entity};
import ${package.Parent}.vo.${entity}VO;
import ${package.Parent}.query.${entity}Query;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import ${superMapperClassPackage};
## 首字母转小写 导包
#set($StringUtilsClass = $package.getClass().forName("org.apache.commons.lang3.StringUtils"))
#if(${mapperAnnotationClass})
import ${mapperAnnotationClass.name};
#end
#set($tableQuery = ${entity}+"Query")
/**
* $!{table.comment} Mapper 接口
*
* @author ${author}
* @since ${date}
*/
@Mapper
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
/**
* 查询所有
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 筛选条件
* @return 对象列表
* */
List<${entity}VO> findAll($!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery}));
}
2.4.8 service.java.vm
package ${package.Service};
import ${package.Parent}.dto.${entity}DTO;
import ${package.Parent}.vo.${entity}VO;
import ${package.Parent}.query.${entity}Query;
import com.github.pagehelper.PageInfo;
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($primaryKey=${field.propertyName})
#set($primaryType=${field.propertyType})
#end
#end
## 首字母转小写
#set($StringUtilsClass = $package.getClass().forName("org.apache.commons.lang3.StringUtils"))
#set($tableQuery = ${table.entityName}+"Query")
/**
*
* $!{table.comment} 服务类
*
* @author ${author}
* @since ${date}
*/
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} {
/**
* 通过${primaryKey}查询单条数据
*
* @param ${primaryKey} 主键
* @return 实例对象
*/
$!{entity}DTO selectByPrimaryKey(${primaryType} ${primaryKey});
/**
* 分页查询
*
* @param pageNum 当前页
* @param pageSize 每页大小
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 筛选条件
* @return 查询结果
*/
PageInfo<$!{entity}VO> queryByPage(int pageNum, int pageSize, $!{entity}Query $StringUtilsClass.uncapitalize(${tableQuery}));
/**
* 新增数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 数据传输对象
* @return 成功新增的数量
*/
int insertSelective($!{entity}Query $StringUtilsClass.uncapitalize(${tableQuery}));
/**
* 修改数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 实例对象
* @return 实例对象
*/
$!{entity}DTO updateByPrimaryKeySelective($!{entity}Query $StringUtilsClass.uncapitalize(${tableQuery}));
/**
* 通过主键删除数据
*
* @param ${primaryKey} 主键
* @return 删除的数据量
*/
int deleteByPrimaryKey(${primaryType} ${primaryKey});
}
#end
2.4.9 serviceImpl.java.vm
package ${package.ServiceImpl};
##定义初始变量
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($primaryKey=${field.propertyName})
#set($primaryType=${field.propertyType})
#end
#end
## 首字母转小写 导包
#set($StringUtilsClass = $package.getClass().forName("org.apache.commons.lang3.StringUtils"))
#set($tableNameImpl = ${entity}+"ServiceImpl")
#set($tableQuery = ${entity}+"Query")
#set($primaryKeyGet = $StringUtilsClass.uncapitalize(${entity})+".get"+$StringUtilsClass.capitalize(${primaryKey})+"()")
import ${package.Parent}.pojo.${entity};
import ${package.Parent}.dto.${entity}DTO;
import ${package.Parent}.vo.${entity}VO;
import ${package.Parent}.query.${entity}Query;
import ${package.Parent}.mapper.$!{entity}Mapper;
import ${package.Parent}.service.$!{entity}Service;
import ${package.Parent}.utils.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import javax.annotation.Resource;
/**
* $!{table.comment}($!{table.name})表服务实现类
*
* @author ${author}
* @since ${date}
*/
@Service("$StringUtilsClass.uncapitalize(${tableNameImpl})")
public class $!{tableNameImpl} implements $!{entity}Service {
private $!{entity}Mapper mapper;
@Autowired
public $!{tableNameImpl}($!{entity}Mapper mapper) {
this.mapper = mapper;
}
/**
* 通过${primaryKey}查询单条数据
*
* @param ${primaryKey} 主键
* @return 实例对象
*/
@Override
public $!{entity}DTO selectByPrimaryKey(${primaryType} ${primaryKey}){
$!{entity} $StringUtilsClass.uncapitalize(${entity}) =this.mapper.selectById($!primaryKey);
$!{entity}DTO $StringUtilsClass.uncapitalize(${entity})DTO = BeanUtils.copyProperties($StringUtilsClass.uncapitalize(${entity}), $!{entity}DTO::new);
return $StringUtilsClass.uncapitalize(${entity})DTO;
}
/**
* 分页查询
*
* @param pageNum 当前页
* @param pageSize 每页大小
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 筛选条件
* @return 查询结果
*/
@Override
public PageInfo<$!{entity}VO> queryByPage(int pageNum,int pageSize, $!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
PageHelper.startPage(pageNum,pageSize);
return new PageInfo<>(this.mapper.findAll($StringUtilsClass.uncapitalize(${tableQuery})));
}
/**
* 新增数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 实例对象
* @return 实例对象
*/
@Override
public int insertSelective($!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
return this.mapper.insert(BeanUtils.copyProperties($StringUtilsClass.uncapitalize(${tableQuery}), ${entity}::new));
}
/**
* 修改数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 实例对象
* @return 实例对象
*/
@Override
public $!{entity}DTO updateByPrimaryKeySelective($!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
$!{entity} $StringUtilsClass.uncapitalize(${entity}) = BeanUtils.copyProperties($StringUtilsClass.uncapitalize(${tableQuery}), ${entity}::new);
int updateNum = this.mapper.updateById($StringUtilsClass.uncapitalize(${entity}));
return this.selectByPrimaryKey(${primaryKeyGet});
}
/**
* 通过主键删除数据
*
* @param ${primaryKey} 主键
* @return 是否成功
*/
@Override
public int deleteByPrimaryKey(${primaryType} ${primaryKey}){
return this.mapper.deleteById(${primaryKey});
}
}
2.4.10 controller.java.vm
package ${package.Controller};
##定义初始变量
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($primaryKey=${field.propertyName})
#set($primaryType=${field.propertyType})
#end
#end
## 首字母转小写 导包
#set($StringUtilsClass = $package.getClass().forName("org.apache.commons.lang3.StringUtils"))
#set($tableNameImpl = ${entity}+"ServiceImpl")
#set($tableQuery = ${entity}+"Query")
#set($primaryKeyGet = $StringUtilsClass.uncapitalize(${entity})+".get"+$StringUtilsClass.capitalize(${primaryKey})+"()")
#set($queryPage = "this."+$StringUtilsClass.uncapitalize(${table.serviceName})+".queryByPage(pageNum,pageSize,"+ $StringUtilsClass.uncapitalize(${tableQuery})+")")
#set($selectByPrimaryKey = "this."+$StringUtilsClass.uncapitalize(${table.serviceName})+".selectByPrimaryKey("+${primaryKey}+")")
#set($insertSelective = "this."+$StringUtilsClass.uncapitalize(${table.serviceName})+".insertSelective("+$StringUtilsClass.uncapitalize(${tableQuery})+")")
#set($updateByPrimaryKeySelective = "this."+$StringUtilsClass.uncapitalize(${table.serviceName})+".updateByPrimaryKeySelective("+$StringUtilsClass.uncapitalize(${tableQuery})+")")
#set($deleteByPrimaryKey = "this."+$StringUtilsClass.uncapitalize(${table.serviceName})+".deleteByPrimaryKey("+${primaryKey}+")")
import ${package.Parent}.dto.${entity}DTO;
import ${package.Parent}.vo.${entity}VO;
import ${package.Parent}.query.${entity}Query;
import ${package.Parent}.service.$!{entity}Service;
import com.personnel.common.utils.BeanUtils;
import com.personnel.common.bean.PageResult;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import javax.annotation.Resource;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import com.github.pagehelper.PageInfo;
/**
* $!{table.comment}($!{table.name})表控制层
*
* @author $!author
* @since ${date}
*/
@RestController
@RequestMapping("/$StringUtilsClass.uncapitalize(${table.entityName.replaceAll("_", "/")})")
@Api(tags = "$!{table.comment}($!{table.name})")
public class ${table.controllerName} {
/**
* 服务对象
*/
@Resource(name = "$StringUtilsClass.uncapitalize(${tableNameImpl})")
private ${table.serviceName} $StringUtilsClass.uncapitalize(${table.serviceName});
/**
* 分页查询$!{table.comment}数据
*
* @param pageNum 当前页
* @param pageSize 每页大小
* @param $StringUtilsClass.uncapitalize(${tableQuery})
* @return 实例对象集合
*/
@ApiOperation(value = "分页查询$!{table.comment}数据" )
@GetMapping("/page/{pageNum}/{pageSize}")
public PageResult<List<$!{entity}VO>> queryByPage(@ApiParam(name = "pageNum", value = "当前页") @PathVariable(name = "pageNum") int pageNum,@ApiParam(name = "pageSize", value = "每页条数") @PathVariable(name = "pageSize") int pageSize,@ApiParam(name = "$StringUtilsClass.uncapitalize(${tableQuery})", value = "$!{table.comment}数据查询对象") $!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
PageInfo<$!{entity}VO> pageInfo = ${queryPage};
return new PageResult<>().success(pageInfo.getList(), pageInfo.getTotal());
}
/**
* 通过主键查询单条$!{table.comment}数据
*
* @param ${primaryKey} 主键
* @return 单条数据
*/
@ApiOperation(value = "通过${primaryKey}主键查询单条$!{table.comment}数据" )
@GetMapping("/{${primaryKey}}")
public PageResult<$!{entity}VO> selectByPrimaryKey(@ApiParam(name = "${primaryKey}", value = "${primaryKey}主键") @PathVariable(name = "${primaryKey}") ${primaryType} ${primaryKey}){
$!{entity}DTO $StringUtilsClass.uncapitalize(${entity})DTO = ${selectByPrimaryKey};
$!{entity}VO $StringUtilsClass.uncapitalize(${entity})VO = BeanUtils.copyProperties($StringUtilsClass.uncapitalize(${entity})DTO, $!{entity}VO::new);
return new PageResult<>().success($StringUtilsClass.uncapitalize(${entity})VO);
}
/**
* 新增单条$!{table.comment}数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 实体
* @return 新增结果
*/
@ApiOperation(value = "新增单条$!{table.comment}数据" )
@PostMapping
public PageResult<Integer> insertSelective(@ApiParam(name = "$StringUtilsClass.uncapitalize(${tableQuery})", value = "$!{tableInfo.comment}对象") @RequestBody $!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
return new PageResult<>().success(${insertSelective});
}
/**
* 通过主键${primaryKey}修改单条$!{table.comment}数据
*
* @param $StringUtilsClass.uncapitalize(${tableQuery}) 实体
* @return 修改结果
*/
@ApiOperation(value = "通过主键${primaryKey}修改单条$!{table.comment}数据" )
@PutMapping
public PageResult<${entity}VO> updateByPrimaryKeySelective(@ApiParam(name = "$StringUtilsClass.uncapitalize(${tableQuery})", value = "$!{table.comment}") @RequestBody $!{tableQuery} $StringUtilsClass.uncapitalize(${tableQuery})){
$!{entity}DTO $StringUtilsClass.uncapitalize(${entity})DTO = ${updateByPrimaryKeySelective};
$!{entity}VO $StringUtilsClass.uncapitalize(${entity})VO = BeanUtils.copyProperties($StringUtilsClass.uncapitalize(${entity})DTO, $!{entity}VO::new);
return new PageResult<>().success($StringUtilsClass.uncapitalize(${entity})VO);
}
/**
* 通过${primaryKey}主键删除单条$!{table.comment}数据
*
* @param ${primaryKey} 主键
* @return 删除结果数
*/
@ApiOperation(value = "通过${primaryKey}主键删除单条$!{table.comment}数据" )
@DeleteMapping("/{${primaryKey}}")
public PageResult<Integer> deleteByPrimaryKey(@ApiParam(name = "${primaryKey}", value = "${primaryKey}主键") @PathVariable(name = "${primaryKey}") ${primaryType} ${primaryKey}){
return new PageResult<>().success(${deleteByPrimaryKey});
}
}
2.5 代码生成器配置
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.builder.CustomFile;
import com.baomidou.mybatisplus.generator.config.po.TableField;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.query.DefaultQuery;
import com.baomidou.mybatisplus.generator.type.TypeRegistry;
import com.example.generator.config.MyDbColumnType;
import org.springframework.util.StringUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Properties;
/**
* @Author
* @Date 2023/8/29 15:32
* @Description
**/
public class Generator {
/**
* 生成文件的作者,可以不填
*/
static String author="";
/**
* 生成的entity、controller、service等包所在的公共上一级包路径全限定名
*/
final static String rootPackage="com.example.generator";
static String dataSourceUrl = "";
static String username = "";
static String password = "";
/**
* 表名,多个使用,分隔
*/
static String tableNames="t_user";
/**
* 要去除的表名前缀
*/
static String tablePrefix="t_";
/**
* 生成的文件输出路径
*/
static String outputDir = System.getProperty("user.dir")+"/src/main/java";
static String xmlOutputDir = System.getProperty("user.dir")+"/src/main/resources/mapper";
/**
* 数据库连接信息为空则获取application.yml文件配置的信息
*/
static String ymlDir = System.getProperty("user.dir")+"/src/main/resources/application.yml";
static {
boolean empty = StringUtils.hasLength(dataSourceUrl) && StringUtils.hasLength(username) && StringUtils.hasLength(password);
if (!empty) {
Properties properties = new Properties();
try (FileInputStream fis = new FileInputStream(ymlDir)) {
properties.load(fis);
} catch (IOException e) {
throw new RuntimeException("application.yml文件加载异常");
}
dataSourceUrl = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
}
}
/**
* 数据源配置
*/
private static final DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(dataSourceUrl, username, password)
.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> typeConvertHandler(globalConfig, typeRegistry, metaInfo))
// 设置SQL查询方式,默认的是元数据查询方式
.databaseQueryClass(DefaultQuery.class).build();
/**
* 策略配置
*/
public static StrategyConfig strategyConfig() {
// 设置需要生成的表名
return new StrategyConfig.Builder().addInclude(tableNames).addTablePrefix(tablePrefix).serviceBuilder().formatServiceFileName("%sService").build();
}
/**
* 模板配置
*/
protected static TemplateConfig.Builder templateConfig() {
return new TemplateConfig.Builder();
}
/**
* 注入配置
*/
protected static InjectionConfig.Builder injectionConfig() {
// 测试自定义输出文件之前注入操作,该操作再执行生成代码前 debug 查看
return new InjectionConfig.Builder().beforeOutputFile((tableInfo, objectMap) -> {
System.out.println("tableInfo: " + tableInfo.getEntityName() + " objectMap: " + objectMap.size());
});
}
/**
* 全局配置
*/
protected static GlobalConfig.Builder globalConfig() {
return new GlobalConfig.Builder().author(author).disableOpenDir().outputDir(outputDir);
}
/**
* 包配置
*/
protected static PackageConfig.Builder packageConfig() {
return new PackageConfig.Builder().parent(rootPackage).pathInfo(Collections.singletonMap(OutputFile.xml, xmlOutputDir));
}
/**
* 自定义数据库类型转换
* @param globalConfig
* @param typeRegistry
* @param metaInfo
* @return
*/
public static IColumnType typeConvertHandler(GlobalConfig globalConfig, TypeRegistry typeRegistry, TableField.MetaInfo metaInfo){
// 包装类转基本数据类型
IColumnType columnType = typeRegistry.getColumnType(metaInfo);
if (MyDbColumnType.TINYINT.getType().equalsIgnoreCase(metaInfo.getTypeName())&&metaInfo.getLength()== 3) {
return DbColumnType.INTEGER;
}
return columnType;
}
/**
* 自定义模板(列表)
*/
public static void testCustomFileByList() {
// 设置自定义输出文件
AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
generator.strategy(strategyConfig());
// 警用默认模板
generator.template(templateConfig().disable(TemplateType.ENTITY
).build());
// 需要生成的自定义文件模板路径
generator.injection(injectionConfig().customFile(new ArrayList<CustomFile>() {{
add(new CustomFile.Builder().fileName("DTO.java").templatePath("/templates/dto.java.vm").packageName("dto").build());
add(new CustomFile.Builder().fileName("VO.java").templatePath("/templates/vo.java.vm").packageName("vo").build());
add(new CustomFile.Builder().fileName("BO.java").templatePath("/templates/bo.java.vm").packageName("bo").build());
add(new CustomFile.Builder().fileName("Query.java").templatePath("/templates/query.java.vm").packageName("query").build());
add(new CustomFile.Builder().fileName(".java").templatePath("/templates/do.java.vm").packageName("entity").build());
}}).build());
generator.global(globalConfig().build());
generator.packageInfo(packageConfig().build());
generator.execute();
}
public static void main(String[] args) {
// 代码生成
testCustomFileByList();
}
}