Mybatis plus 号称是只做增强不做改变,可快速进行单表 CRUD 操作,从而节省大量时间,具备代码生成、物理分页、性能分析等功能,官网地址教程地址 https://baomidou.com/guide/
PS:其实mybatis本身就可以自动生成一些常用代码和单表查询的语句,只是没有mp功能这么多而已,不过,快速开发的时候mp还是很给力的。
背景:
项目中,采用了springboot框架,集成mybatis plus的时候,有很多不如意的地方,于是就游走与官网和社区中,仓促写了个代码生成器,以备后用
项目依赖
<!--BaseDO所在项目(自动代码生成的entity、mapper、service等都在该项目中)-->
<dependency>
<groupId>com.study.common</groupId>
<artifactId>common</artifactId>
<version>1.0.0</version>
</dependency>
<!--主要是使用swagger2自动生成model注视-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.1</version>
<exclusions>
<exclusion>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--模版支持-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.1</version>
</dependency>
<!--数据库支持-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--setter/getter支持-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--自动代码支持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
配置
全局配置
- 设置代码生成后是否打开生成代码所在目录
- 设置是否要覆盖同名文件
- 设置XML中是否要生成ResultMap映射(就是MybatisPlus默认给你当前表所有字段的ResultMap)
- 设置是否生成基本的表字段的视图(就是 id,…)
- 设置是否需要Swagger注解(就是@ApiOperation注解,用来生成文档)
- 设置作者名,用于类注释和方法注释
- 设置生成类的别名(如%sDao表示原来的类名后面跟上Dao)
/**
* 全局配置
*
* @return
*/
private static GlobalConfig globalConfig() {
return new GlobalConfig()
// 打开文件
.setOpen(false)
// 文件覆盖
.setFileOverride(true)
// 开启activeRecord模式
.setActiveRecord(false)
// XML ResultMap: mapper.xml生成查询映射结果
.setBaseResultMap(true)
// XML ColumnList: mapper.xml生成查询结果列
.setBaseColumnList(true)
// swagger注解; 须添加swagger依赖
.setSwagger2(true)
// 作者
.setAuthor(AUTHOR)
// 设置实体类名称
.setEntityName("%sDO")
//设置服务类名称
.setServiceName("%sService")
;
}
数据源配置
这个相对比较简单
/**
* 数据源配置
*
* @return
*/
private static DataSourceConfig dataSourceConfig() {
return new DataSourceConfig()
// 数据库类型
.setDbType(DB_TYPE)
// 连接驱动
.setDriverName(driverClassName)
// 地址
.setUrl(url)
// 用户名
.setUsername(username)
// 密码
.setPassword(password);
}
配置文件策略
- 设置表名和表字段生成策略(由于数据库中通常都是下划线命名,但是Java中通常是驼峰命名,所以通常会配置下划线转驼峰的配置)
- 设置需要生成代码的表
- 设置生成代码时需要去掉的前缀(因为表中常常会有前缀 info_ dict_ relation_ 之类特殊含义的前缀,可以按需调整)
- 设置是否需要Lombok注解
- 设置是否需要构建者模式
- 设置是否需要生成表信息注解
- 设置特殊意义字段
- 逻辑删除
- 自动更新插入
- 版本迭代字段
- 设置父类中字段
- 如 id version deleted类似的字段几乎是每一张表都会有的字段,这样就可以设置一个共同的父类来保存这些字段
- 设置父类,也就是某个类需要继承的类
/**
* 设置表格/字段等生成策略配置
*
* @return
*/
private static StrategyConfig strategyConfig() {
StrategyConfig strategyConfig = new StrategyConfig()
// 表名生成策略:下划线连转驼峰
.setNaming(NamingStrategy.underline_to_camel)
// 表字段生成策略:下划线连转驼峰
.setColumnNaming(NamingStrategy.underline_to_camel)
// 需要生成的表
.setInclude(TABLES)
// 生成controller
.setRestControllerStyle(true)
// 去除表前缀
.setTablePrefix(ENTITY_IGNORE_PREFIX)
// controller映射地址:驼峰转连字符
.setControllerMappingHyphenStyle(false)
// 是否启用builder 模式
.setChainModel(true)
// 是否为lombok模型; 需要lombok依赖
.setEntityLombokModel(true)
// 生成实体类字段注解
.setEntityTableFieldAnnotationEnable(true)
// 乐观锁、逻辑删除、表填充
// .setVersionFieldName("version")
.setLogicDeleteFieldName("is_deleted")
.setTableFillList(Arrays.asList(
new TableFill("gmt_modified", FieldFill.UPDATE),
new TableFill("gmt_create", FieldFill.INSERT)
))
// 生成类的时候排除的字符串(因为这些字段已经在父类中,子类无需再生成)
.setSuperEntityColumns(
SUPER_ENTITY_COLUMNS
);
//配置父类
if (Objects.nonNull(SUPER_ENTITY_CLASS) && SUPER_ENTITY_CLASS.length() > 0) {
strategyConfig.setSuperEntityClass(SUPER_ENTITY_CLASS);
}
return strategyConfig;
}
PS : 至此,使用mybatis plus官方模版已经可以生成自动代码了,不过生成的代码并不符合我的要求。我需要自定义生成文件的路劲和自定义生成类
建表语句
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for shop_order
-- ----------------------------
DROP TABLE IF EXISTS `shop_order`;
CREATE TABLE `shop_order` (
`id` int(11) NOT NULL,
`mount` decimal(20, 0) NULL DEFAULT NULL COMMENT '金额',
`status` tinyint(1) NOT NULL COMMENT '状态 0:未支付 1:已经支付',
`importance` tinyint(5) UNSIGNED NULL DEFAULT 0 COMMENT '排序字段',
`creator` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '创建人',
`modifier` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '修改人',
`gmt_create` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`gmt_modified` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
`is_deleted` tinyint(1) UNSIGNED NULL DEFAULT 0 COMMENT '是否被删除 1:数据无效(被删除) 0:数据有效',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;
BaseDO实体
package com.study.common.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* @Package: com.study.common.entity
* @Description: <基础实体类>
* @Author: milla
* @CreateDate: 2020/11/06 14:56
* @UpdateUser: milla
* @UpdateDate: 2020/11/06 14:56
* @UpdateRemark: <>
* @Version: 1.0
*/
@ApiModel(value = "基础实体")
@Data
public abstract class BaseDO implements Serializable {
/**
* 序列化时候使用
*/
private static final long serialVersionUID = 1L;
/**
* 排序字段
*/
@ApiModelProperty(value = "排序字段", notes = "默认按照记录id正序输出")
@TableField(fill = FieldFill.INSERT_UPDATE, update = "12")
private Integer importance;
/**
* 创建人
*/
@ApiModelProperty(value = "创建人", notes = "")
private String creator;
/**
* 修改人
*/
@ApiModelProperty(value = "修改人", notes = "")
private String modifier;
/**
* 数据创建时间
*/
@ApiModelProperty(value = "数据创建时间", notes = "")
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
/**
* 数据被修改时间
*/
@ApiModelProperty(value = "数据被修改时间", notes = "")
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
@TableLogic
/**
* 数据是否被删除
*/
@ApiModelProperty(value = "数据是否被删除", notes = "逻辑删除字段")
protected Integer isDeleted;
@TableId(value = "id", type = IdType.AUTO)
/**
*主键自增
*/
@ApiModelProperty(value = "主键自增", notes = "")
protected Long id;
}
QueryBO-查询实体
package com.study.common.entity.bo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Package: com.study.common.entity.bo
* @Description: <查询实体类>
* @Author: milla
* @CreateDate: 2020/11/06 14:56
* @UpdateUser: milla
* @UpdateDate: 2020/11/06 14:56
* @UpdateRemark: <>
* @Version: 1.0
*/
@Data
@ApiModel(value = "查询实体类")
public class QueryBO<T> {
@ApiModelProperty(value = "分页实体", notes = "包含分页参数")
private Page<T> page;
@ApiModelProperty(value = "实体参数", notes = "")
private T entity;
public QueryWrapper initQueryWrapper() {
QueryWrapper wrapper = new QueryWrapper<>();
wrapper.setEntity(entity);
return wrapper;
}
}
常量类
BasicConstant
主要用于配置表格和数据库信息
package com.study.generator;
import com.baomidou.mybatisplus.annotation.DbType;
/**
* @Package: com.study.generator
* @Description: <表格和数据源配置项>
* @Author: milla
* @CreateDate: 2020/11/12 17:48
* @UpdateUser: milla
* @UpdateDate: 2020/11/12 17:48
* @UpdateRemark: <>
* @Version: 1.0
*/
public final class BasicConstant {
/**
* 作者
*/
public static String AUTHOR = "milla" ;
/**
* 生成的实体类忽略表前缀: 不需要则置空
*/
public static String ENTITY_IGNORE_PREFIX = "settings_" ;
/**
* 表名
*/
public static String[] TABLES = {
"shop_order"
};
/**
* 实体类的父类Entity
*/
public static String SUPER_ENTITY_CLASS = "com.aimsphm.nuclear.common.entity.BaseDO" ;
/**
* 基础父类继承字段
*/
public static String[] SUPER_ENTITY_COLUMNS = {
"id", "creator", "modifier", "gmt_modified", "gmt_create", "is_deleted", "importance"
};
/**
* 数据库
*/
public static String username = "root" ;
public static String password = "*****" ;
public static String url = "jdbc:mysql://192.168.1.1:3306/test?useAffectedRows=true&allowMultiQueries=true&characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=Asia/Shanghai&autoReconnect=true&failOverReadOnly=false&maxReconnects=10" ;
public static DbType DB_TYPE = DbType.MYSQL;
public static String driverClassName = "com.mysql.cj.jdbc.Driver" ;
}
PackageInfoConstant
- 主要用来配置文件生成路径及java类的包路径
package com.study.generator;
import java.io.File;
/**
* @Package: com.study.generator
* @Description: <自定义生成的文件路径和包路径>
* @Author: milla
* @CreateDate: 2020/11/12 17:47
* @UpdateUser: milla
* @UpdateDate: 2020/11/12 17:47
* @UpdateRemark: <>
* @Version: 1.0
*/
public final class PackageInfoConstant {
//------------------------------可能需要修改的配置---------------------------------------------
/**
* ------------------------------controller层单独配置--------------------------
*/
public static String CONTROLLER_OUTPUT_ROOT_PATH = "/com/study/" ;
/**
* controller根目录
*/
public static String ROOT_PROJECT_CONTROLLER_PATH = "/core" ;
/**
* 包路径(基础包路径)
*/
public static String PACKAGE_ROOT = "com.study" ;
/**
* Controller模块(笔者的controller和service,dao,entity不在同一个项目中)
*/
public static String CONTROLLER_MODULE_NAME = "core" ;
/**
* controller的包路径
*/
public static String PACKAGE_CONTROLLER = PACKAGE_ROOT + "." + CONTROLLER_MODULE_NAME + ".controller" ;
/**
* ------------------------------controller以外的其他配置--------------------------
*/
/**
* 根路径
*/
public static String ROOT_PROJECT_PATH = "/common" ;
/**
* 输出基础包
*/
public static String OUTPUT_ROOT_PATH = "/com/study/" ;
/**
* 配置你包路径下的子模块(例如:com.study.common)
* (例如:com.study.ext)
*/
public static String MODULE_NAME = "common" ;
/**
* 扩展模块
*/
public static String EXT_MODULE_NAME = "ext" ;
public static String PACKAGE_PARENT = PACKAGE_ROOT + "." + MODULE_NAME;
/**
* 扩展基础包路径
*/
public static String PACKAGE_PARENT_EXT = PACKAGE_ROOT + "." + EXT_MODULE_NAME;
//---------------------------------------------------------------------------
/**
* 扩展的实体名称
*/
public static String EXT_ClASS_NAME = "Ext" ;
/**
* 扩展实现类名称
*/
public static String EXT_SERVICE_CLASSNAME_IMPL = "ExtImpl" ;
/**
* 生成文件路径、Dao、XML、Service、Controller
* 父包名路径(文件输出路径,也是导包的路径)
*/
public static String ENTITY_PATH = "/entity/" ;
public static String MAPPER_PATH = "/mapper/" ;
public static String XML_PATH = "/resources/mapper/" ;
public static String XML_PATH_EXT = "/resources/mapper/ext/" ;
public static String SERVICE_PATH = "/service/" ;
public static String SERVICE_IMPL_PATH = "/service/impl/" ;
public static String CONTROLLER_PATH = "/controller/" ;
public static String MAIN_ROOT = "/src/main/" ;
public static String JAVA_ROOT = MAIN_ROOT + "/java/" ;
/**
* 包路径中的mapper,service,impl等常量
*/
public static String PACKAGE_NAME_MAPPER = "mapper" ;
public static String PACKAGE_NAME_SERVICE = "service" ;
public static String PACKAGE_NAME_IMPL = "impl" ;
/**
* mapper.xml输出模块路径(需要注意放置的位置:默认从模块/src/main下开始)
*/
public static String XML_OUTPUT_MODULE = File.separator + MODULE_NAME;
public static String XML_OUTPUT_PATH = ROOT_PROJECT_PATH + MAIN_ROOT + XML_PATH;
/**
* IService.java, serviceImpl.java输出模块路径
*/
public static String SERVICE_OUTPUT_MODULE = File.separator + MODULE_NAME;
public static String SERVICE_OUTPUT_PATH = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + SERVICE_OUTPUT_MODULE + SERVICE_PATH;
public static String SERVICE_IMPL_OUTPUT_PATH = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + SERVICE_OUTPUT_MODULE + SERVICE_IMPL_PATH;
/**
* Entity.java, Mapper.java, Mapper.xml输出模块路径
*/
public static String DAO_OUTPUT_MODULE = File.separator + MODULE_NAME;
public static String ENTITY_OUTPUT_PATH = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + DAO_OUTPUT_MODULE + ENTITY_PATH;
public static String MAPPER_OUTPUT_PATH = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + DAO_OUTPUT_MODULE + MAPPER_PATH;
/**
* ----------------------------------------EXT_CONFIG----------------------------------------------------
*/
/**
* mapper.xml输出模块路径(需要注意放置的位置:默认从模块/src/main下开始)
*/
public static String XML_OUTPUT_PATH_EXT = ROOT_PROJECT_PATH + MAIN_ROOT + XML_PATH_EXT;
/**
* IService.java, serviceImpl.java输出模块路径
*/
public static String SERVICE_OUTPUT_MODULE_EXT = File.separator + EXT_MODULE_NAME;
public static String SERVICE_OUTPUT_PATH_EXT = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + SERVICE_OUTPUT_MODULE_EXT + SERVICE_PATH;
public static String SERVICE_IMPL_OUTPUT_PATH_EXT = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + SERVICE_OUTPUT_MODULE_EXT + SERVICE_IMPL_PATH;
/**
* Entity.java, Mapper.java, Mapper.xml输出模块路径
*/
public static String DAO_OUTPUT_MODULE_EXT = File.separator + EXT_MODULE_NAME;
public static String MAPPER_OUTPUT_PATH_EXT = ROOT_PROJECT_PATH + JAVA_ROOT + OUTPUT_ROOT_PATH + DAO_OUTPUT_MODULE_EXT + MAPPER_PATH;
/**
* ----------------------------------------EXT_CONFIG----------------------------------------------------
*/
/**
* Controller.java输出模块路径
*/
public static String CONTROLLER_OUTPUT_MODULE = File.separator + CONTROLLER_MODULE_NAME;
public static String CONTROLLER_OUTPUT_PATH = ROOT_PROJECT_CONTROLLER_PATH + JAVA_ROOT + CONTROLLER_OUTPUT_ROOT_PATH + CONTROLLER_OUTPUT_MODULE + CONTROLLER_PATH;
}
TemplateConstant
- 主要用来配置模版位置
package com.study.generator;
/**
* @Package: com.study.generator
* @Description: <模版位置配置类>
* @Author: milla
* @CreateDate: 2020/11/12 18:09
* @UpdateUser: milla
* @UpdateDate: 2020/11/12 18:09
* @UpdateRemark: <>
* @Version: 1.0
*/
public final class TemplateConstant {
/**
* entity输出模板
*/
public static String ENTITY_TEMPLATE = "templates/entity.java.vm" ;
/**
* mapper.xml输出模板
*/
public static String XML_TEMPLATE = "templates/mapper.xml.vm" ;
/**
* mapper.java输出模板
*/
public static String MAPPER_TEMPLATE = "templates/mapper.java.vm" ;
/**
* service输出模板
*/
public static String SERVICE_TEMPLATE = "templates/service.java.vm" ;
/**
* serviceImpl输出模板
*/
public static String SERVICE_IMPL_TEMPLATE = "templates/serviceImpl.java.vm" ;
/**
* mapperExt.xml输出模板
*/
public static String XML_TEMPLATE_EXT = "templates/mapperExt.xml.vm" ;
/**
* mapperExt.java输出模板
*/
public static String MAPPER_TEMPLATE_EXT = "templates/mapperExt.java.vm" ;
/**
* serviceExt输出模板
*/
public static String SERVICE_TEMPLATE_EXT = "templates/serviceExt.java.vm" ;
/**
* serviceExtImpl输出模板
*/
public static String SERVICE_IMPL_TEMPLATE_EXT = "templates/serviceExtImpl.java.vm" ;
/**
* controller输出模板
*/
public static String CONTROLLER_TEMPLATE = "templates/controller.java.vm" ;
}
模版配置
- 因为要生成entity,mapper,mapperExt,mapper.xml,mapperExt.xml,service,serviceExt,serviceImpl,serviceIExtmpl,controller
- 所以会有多个模版
entity.java.vm
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
import lombok.Data;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* @Package: ${package.Entity}
* @Description: <$!{table.comment}实体>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
@Data
#if(${cfg.customerTableName})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value = "$!{table.comment}实体")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end {
#else
public class ${entity} implements Serializable{
#end
/**
* 序列化时候使用
*/
private static final long serialVersionUID = ${cfg.get(${table.name})}L;
## ---------- BEGIN 字段循环遍历 ----------
#foreach($field in ${table.fields})
#if(${field.keyFlag})
#set($keyPropertyName=${field.propertyName})
#end
###if("$!field.comment" != "")
## /**
## * ${field.comment}
## */
###end
#if(${field.keyFlag})
## 主键
#if(${field.keyIdentityFlag})
@TableId(value = "${field.name}", type = IdType.AUTO)
#elseif(${field.convert})
@TableId("${field.name}")
#end
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
## 生成swagg实体描述
#if(${swagger2})
#set($comment=${field.comment})
#set($blankIndex=$comment.indexOf(" "))
#set($hasBlank=$comment.indexOf(" ")!=-1)
#if($hasBlank)
@ApiModelProperty(value = "$comment.substring(0,$blankIndex)", notes = "$comment.substring($blankIndex).trim()")
#end
#if(!$hasBlank)
@ApiModelProperty(value = "$comment", notes = "")
#end
#end
private ${field.propertyType} ${field.propertyName};
#end
}
mapper.java.vm
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
/**
* @Package: ${package.Mapper}
* @Description: <$!{table.comment}Mapper接口>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
#if(${kotlin})
interface ${table.mapperName} : ${superMapperClass}<${entity}>
#else
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
}
#end
mapperExt.java.vm
package ${cfg.extPackagePre}.${cfg.mapper};
import ${package.Mapper}.${table.mapperName};
/**
* @Package: ${cfg.extPackagePre}.${cfg.mapper}
* @Description: <$!{table.comment}MapperExt接口>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
#if(${kotlin})
interface ${table.mapperName}${cfg.classNameExt} : ${superMapperClass}<${entity}>
#else
public interface ${table.mapperName}${cfg.classNameExt} extends ${table.mapperName} {
}
#end
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}">
#if(${enableCache})
<!-- 开启二级缓存 -->
<cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
#end
#if(${baseResultMap})
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="${package.Entity}.${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>
#end
#if(${baseColumnList})
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
#foreach($field in ${table.commonFields})
${field.columnName},
#end
${table.fieldNames}
</sql>
#end
</mapper>
mapperExt.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="${cfg.extPackagePre}.${cfg.mapper}.${table.mapperName}${cfg.classNameExt}">
#if(${enableCache})
<!-- 开启二级缓存 -->
<cache type="org.mybatis.caches.ehcache.LoggingEhcache"/>
#end
#if(${baseResultMap})
<!-- 扩展查询映射结果 -->
<resultMap id="BaseResultMap${cfg.classNameExt}" type="${package.Entity}.${entity}"
extends="${package.Mapper}.${table.mapperName}.BaseResultMap">
</resultMap>
#end
#if(${baseColumnList})
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
#foreach($field in ${table.commonFields})
${field.columnName},
#end
${table.fieldNames}
</sql>
#end
</mapper>
service.java.vm
package ${package.Service};
import ${package.Entity}.${entity};
import ${superServiceClassPackage};
/**
* @Package: ${package.Service}
* @Description: <$!{table.comment}服务类>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
#if(${kotlin})
interface ${table.serviceName} : ${superServiceClass}<${entity}>
#else
public interface ${table.serviceName} extends ${superServiceClass}<${entity}> {
}
#end
serviceExt.java.vm
package ${cfg.extPackagePre}.${cfg.service};
import ${package.Service}.${table.serviceName};
/**
* @Package: ${cfg.extPackagePre}.${cfg.service}
* @Description: <$!{table.comment}扩展服务类>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
#if(${kotlin})
interface ${table.serviceName}${cfg.classNameExt} : ${table.serviceName}
#else
public interface ${table.serviceName}${cfg.classNameExt} extends ${table.serviceName} {
}
#end
serviceImpl.java.vm
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* @Package: ${package.ServiceImpl}
* @Description: <$!{table.comment}服务实现类>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
##@Service
#if(${kotlin})
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
}
#else
##@ConditionalOnProperty(prefix = "spring.config-service", name = "enable", havingValue = "true", matchIfMissing = false)
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> implements ${table.serviceName} {
}
#end
serviceExtImpl.java.vm
package ${cfg.extPackagePre}.${cfg.service}.${cfg.serviceImpl};
import ${cfg.extPackagePre}.${cfg.service}.${table.serviceName}${cfg.classNameExt};
import ${package.ServiceImpl}.${table.serviceImplName};
import org.springframework.stereotype.Service;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
/**
* @Package: ${cfg.extPackagePre}.${cfg.service}.${cfg.serviceImpl}
* @Description: <$!{table.comment}扩展服务实现类>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
@Service
#if(${kotlin})
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {
}
#else
@ConditionalOnProperty(prefix = "spring.config-service", name = "enable", havingValue = "true")
public class ${table.serviceName}${cfg.serviceImplNameFix} extends ${table.serviceImplName} implements ${table.serviceName}${cfg.classNameExt} {
}
#end
controller.java.vm
package ${cfg.controllerPackage};
import ${package.Entity}.${entity};
##import ${package.Service}.${table.serviceName};
import ${package.Entity}.bo.QueryBO;
import ${cfg.extPackagePre}.${cfg.service}.${table.serviceName}${cfg.classNameExt};
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @Package: ${cfg.controllerPackage}
* @Description: <$!{table.comment}前端控制器>
* @Author: ${author}
* @CreateDate: ${date}
* @UpdateUser: ${author}
* @UpdateDate: ${date}
* @UpdateRemark: <>
* @Version: 1.0
*/
#set($path=${table.name})
#set($path=$path.concat("path"))
@RestController
@Api(tags = "$!{table.comment}-相关接口")
@RequestMapping(value = "${cfg.get($path)}", produces = MediaType.APPLICATION_JSON_VALUE)
public class ${table.controllerName} {
## @Autowired
## private ${table.serviceName} i${table.serviceName};
@Autowired
private ${table.serviceName}${cfg.classNameExt} i${table.serviceName}${cfg.classNameExt};
@GetMapping("list")
@ApiOperation(value = "$!{table.comment}分页查询")
public Page<${entity}> list${table.serviceName}ByPage(QueryBO<${entity}> query) {
return i${table.serviceName}${cfg.classNameExt}.page(query.getPage() == null ? new Page() : query.getPage(), query.initQueryWrapper());
}
@GetMapping("{id}")
@ApiOperation(value = "$!{table.comment}获取某一实体")
public ${entity} get${table.serviceName}Details(@PathVariable Long id) {
return i${table.serviceName}${cfg.classNameExt}.getById(id);
}
@PostMapping
@ApiOperation(value = "$!{table.comment}新增数据")
public boolean save${table.serviceName}(@RequestBody ${entity} dto) {
return i${table.serviceName}${cfg.classNameExt}.save(dto);
}
@PutMapping("{id}")
@ApiOperation(value = "$!{table.comment}修改数据")
public boolean modify${table.serviceName}(@RequestBody ${entity} dto, @PathVariable Long id) {
dto.setId(id);
return i${table.serviceName}${cfg.classNameExt}.updateById(dto);
}
@DeleteMapping("batch")
@ApiOperation(value = "$!{table.comment}批量删除数据")
public boolean batchRemove${table.serviceName}(@RequestParam(value = "ids") List<Long> ids) {
return i${table.serviceName}${cfg.classNameExt}.removeByIds(ids);
}
@DeleteMapping("{id}")
@ApiOperation(value = "$!{table.comment}删除数据")
public boolean remove${table.serviceName}(@PathVariable Long id) {
return i${table.serviceName}${cfg.classNameExt}.removeById(id);
}
}
★重点: 自定义输出路径
/**
* 自定义配置
*
* @param isCreateExt
*/
private static InjectionConfig injectionConfig(boolean isCreateExt) {
return new InjectionConfig() {
@Override
public void initMap() {
// 注入配置
Map<String, Object> map = new HashMap<>(16);
//指定表格的名称
map.put("customerTableName", true);
//需要生成扩展的时候执行
if (isCreateExt) {
//扩展的表名
map.put("extPackagePre", PACKAGE_PARENT_EXT);
//controller包路径
map.put("controllerPackage", PACKAGE_CONTROLLER);
//service包名
map.put("service", PACKAGE_NAME_SERVICE);
//实现类包名
map.put("serviceImpl", PACKAGE_NAME_IMPL);
//mapper包名
map.put("mapper", PACKAGE_NAME_MAPPER);
//实现后缀
map.put("serviceImplNameFix", EXT_SERVICE_CLASSNAME_IMPL);
//类名扩展后缀
map.put("classNameExt", EXT_ClASS_NAME);
}
for (String tableName : TABLES) {
//指定每个类的serialVersionUID
long serialVersionUID = UUID.nameUUIDFromBytes(tableName.getBytes()).getLeastSignificantBits();
map.put(tableName, Math.abs(serialVersionUID));
//计算controller的路径
StringBuilder sb = new StringBuilder();
if (StringUtils.contains(tableName, UNDERSCORE)) {
sb.append(SLASH + tableName.substring(0, tableName.indexOf(UNDERSCORE)));
String url = tableName.substring(tableName.indexOf(UNDERSCORE) + 1);
sb.append(SLASH + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, url));
map.put(tableName + "path", sb.toString());
continue;
}
sb.append(SLASH + CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, url));
map.put(tableName + "path", sb.toString());
}
this.setMap(map);
}
}
// 判断是否创建文件
.setFileCreate((configBuilder, fileType, filePath) -> {
// 检查文件目录,不存在自动递归创建
File file = new File(filePath);
boolean exist = file.exists();
if (!exist) {
file.getParentFile().mkdirs();
}
return true;
})
// 自定义输出文件
.setFileOutConfigList(fileOutConfigList(isCreateExt));
}
/**
* 自定义输出路径
*
* @param isCreateExt
* @return
*/
private static List<FileOutConfig> fileOutConfigList(boolean isCreateExt) {
List<FileOutConfig> list = new ArrayList<>();
// 当前项目路径
String projectPath = System.getProperty("user.dir");
// 实体类文件输出
list.add(new FileOutConfig(ENTITY_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + ENTITY_OUTPUT_PATH + tableInfo.getEntityName() + StringPool.DOT_JAVA;
}
});
// mapper xml文件输出
list.add(new FileOutConfig(XML_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + XML_OUTPUT_PATH + tableInfo.getMapperName() + StringPool.DOT_XML;
}
});
// mapper文件输出
list.add(new FileOutConfig(MAPPER_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + MAPPER_OUTPUT_PATH + tableInfo.getMapperName() + StringPool.DOT_JAVA;
}
});
// service文件输出
list.add(new FileOutConfig(SERVICE_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + SERVICE_OUTPUT_PATH + tableInfo.getServiceName() + StringPool.DOT_JAVA;
}
});
// service impl文件输出
list.add(new FileOutConfig(SERVICE_IMPL_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + SERVICE_IMPL_OUTPUT_PATH + tableInfo.getServiceImplName() + StringPool.DOT_JAVA;
}
});
//----------------------------------------------是否需要生成扩展------------------------------------------------------
if (isCreateExt) {
// mapperExt xml文件输出
list.add(new FileOutConfig(XML_TEMPLATE_EXT) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + XML_OUTPUT_PATH_EXT + tableInfo.getMapperName() + EXT_ClASS_NAME + StringPool.DOT_XML;
}
});
// mapperExt文件输出
list.add(new FileOutConfig(MAPPER_TEMPLATE_EXT) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + MAPPER_OUTPUT_PATH_EXT + tableInfo.getMapperName() + EXT_ClASS_NAME + StringPool.DOT_JAVA;
}
});
// serviceExt文件输出
list.add(new FileOutConfig(SERVICE_TEMPLATE_EXT) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + SERVICE_OUTPUT_PATH_EXT + tableInfo.getServiceName() + EXT_ClASS_NAME + StringPool.DOT_JAVA;
}
});
// serviceExt impl文件输出
list.add(new FileOutConfig(SERVICE_IMPL_TEMPLATE_EXT) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + SERVICE_IMPL_OUTPUT_PATH_EXT + tableInfo.getServiceName() + EXT_SERVICE_CLASSNAME_IMPL + StringPool.DOT_JAVA;
}
});
//----------------------------------------------------------------------------------------------------
}
// controller文件输出
list.add(new FileOutConfig(CONTROLLER_TEMPLATE) {
@Override
public String outputFile(TableInfo tableInfo) {
return projectPath + CONTROLLER_OUTPUT_PATH + tableInfo.getControllerName() + StringPool.DOT_JAVA;
}
});
return list;
}
执行生成程序
public static void main(String[] args) {
boolean isCreateExt = true;
builder(isCreateExt);
}
/**
* 执行自动代码生成程序
*
* @param isCreateExt
*/
private static void builder(boolean isCreateExt) {
new AutoGenerator().setGlobalConfig(globalConfig())
.setDataSource(dataSourceConfig())
.setStrategy(strategyConfig())
.setCfg(injectionConfig(isCreateExt))
.setPackageInfo(
new PackageConfig()
.setParent(PACKAGE_PARENT)
)
.execute();
}
生成的效果如下
总结
1.通过自定义模版,生成不同的文件到不同的项目中,使得实体等文件可以被重复利用
2.生成扩展的ext文件,下次再生成的时候,可以不覆盖ext文件,表结构改动之后基础文件可快速生成并覆盖旧文件
3.因为是根据表名生成序列号,不会存在反序列化的问题
4.自定义业务实现和多表复杂业务都可放在ext扩展类中
5.springboot可采用@ConditionalOnProperty注解启用某些特定的service