zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Springboot +mybatis-plus 实现代码生成器(自定义默认模板引擎)

SpringBoot模板引擎mybatis 实现 自定义 默认 Plus
2023-09-11 14:20:19 时间

一、前言

1.在MyBatis-Plus的官网就为我们提供了代码生成器的使用教程,它可以为完成最基本的代码生成,但是想要实现自己的效果,还需要我们自己去设置生成模板。

2.AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率。

3.项目架构
在这里插入图片描述

二、实现过程

1.添加依赖

MyBatis-Plus 从 3.0.3 之后移除了代码生成器与模板引擎的默认依赖,需要手动添加相关依赖:

  • 添加 代码生成器 依赖
<!--mybatis-plus代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>

  • 添加 自定义模板引擎 依赖
<!--mybatis-plus逆向功能所需的模板引擎(自定义模板引擎)-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.30</version>
</dependency>
<!--velocity 模板引擎,Mybatis Plus 代码生成器需要(默认模板引擎)-->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity-engine-core</artifactId>
    <version>2.3</version>
</dependency>

注意:如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。

AutoGenerator generator = new AutoGenerator();

// set freemarker engine
generator.setTemplateEngine(new FreemarkerTemplateEngine());

// set beetl engine
generator.setTemplateEngine(new BeetlTemplateEngine());

// set custom engine (reference class is your custom engine class)
generator.setTemplateEngine(new CustomTemplateEngine());

// other config
...

2.复制maven包内的模板

maven仓库内搜索mybatis-plus-generator,打开,复制其中的templates目录至自己项目的resources下

在这里插入图片描述
由于我们后续使用的是FreemarkerTemplateEngine,仅用到.ftl文件,其他后缀文件可自行删除。

3.自定义模板内容

在上面模板的基础上,修改编写自己所需的模板内容:
controller.java.ftl

package ${package.Controller};

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import ${package.Entity}.${entity};
import ${package.Service}.${table.serviceName};
import java.util.Date;
<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>

import ${package.ModuleName}.utils.*;

/**
* ${table.comment!} 前端控制器
*
* @author ${author}
* @since ${date}
*/
@Api(tags = "${entity}API")
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName??>/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
 class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
 <#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
 <#else>
public class ${table.controllerName} {
 </#if>
    @Autowired
    private ${table.serviceName} service;

    @ApiOperation(value = "分页列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "currentPage",value = "当前页码",paramType = "query",dataType = "int"),
        @ApiImplicitParam(name = "pageSize",value = "每页显示个数",paramType = "query",dataType = "int")
    })
    @PostMapping(value = "/page")
    public Result page(@RequestBody ${entity} entity,@RequestParam Integer currentPage,@RequestParam Integer pageSize){

        Page<${entity}> page = new Page<>(currentPage,pageSize);
        QueryWrapper<${entity}> queryWrapper = new QueryWrapper();
        //分页查看列表
        IPage<${entity}> pageInfo = service.selectPage(page,queryWrapper);
        //返回结果集
        return new Result().ok(new PageData<>(pageInfo.getRecords(),pageInfo.getTotal()));
    }

    /**
    * 添加信息
    */
    @PostMapping("add")
    @ApiOperation("添加信息")
    public Result add(@RequestBody ${entity} entity){
        entity.setCreate_by(1);
        entity.setCreate_time(new Date());
        int n = service.add(entity);
        if(n>0){
            return new Result();
        }else{
            return new Result().error(400,"添加失败");
        }
    }

    /**
    * 编辑信息
    */
    @PostMapping("update")
    @ApiOperation("添加信息")
    public Result update(@RequestBody ${entity} entity){
        entity.setUpdate_by(1);
        entity.setUpdate_time(new Date());
        int n = service.update(entity);
        if(n>0){
            return new Result();
        }else{
            return new Result().error(400,"编辑失败");
        }
    }


    /**
    * 根据ids删除信息
    */
    @PostMapping("del")
    @ApiOperation("根据ids删除信息")
    public Result del(@RequestBody Integer[] ids){
        int n = service.deleteBatchIds(ids);
        if(n>0){
            return new Result();
        }else{
            return new Result().error(400,"删除失败");
        }
    }
}
</#if>



service.java.ftl

package ${package.Service};

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Entity}.${entity};
import java.util.List;

/**
 * <p>
 * ${table.comment!} 服务类
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if kotlin>
interface ${table.serviceName} : ${superServiceClass}<${entity}>
<#else>
public interface ${table.serviceName}{

  /**
   * 添加信息
   * @param entity
   * @return
  */
  int add(${entity} entity);

  /**
   * 编辑信息
   * @param entity
   * @return
  */
  int update(${entity} entity);

  /**
   * 获取列表信息
   * @param queryWrapper
   * @return
  */
  List<${entity}> selectList(Wrapper<${entity}> queryWrapper);

  /**
   * 分页获取列表信息
   * @return
  */
  IPage<${entity}> selectPage(Page page, Wrapper<${entity}> queryWrapper);

  /**
   * 批量删除商品
   * @param ids
   * @return
  */
  int deleteBatchIds(Integer[] ids);
}
</#if>


serviceImpl.java.ftl

package ${package.ServiceImpl};

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Arrays;
import java.util.List;

/**
 * <p>
 * ${table.comment!} 服务实现类
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
@Service
<#if kotlin>
open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} {

}
<#else>
public class ${table.serviceImplName}  implements ${table.serviceName} {

   @Autowired
   private ${table.mapperName} mapper;

    /**
      * 添加信息
      * @param entity
      * @return
    */
    @Override
    public int add(${entity} entity) {
        return mapper.insert(entity);
    }

    /**
      * 编辑信息
      * @param entity
      * @return
    */
    @Override
    public int update(${entity} entity) {
        return mapper.updateById(entity);
    }

    /**
    * 获取列表信息
    * @param queryWrapper
    * @return
    */
    @Override
    public List<${entity}> selectList(Wrapper<${entity}> queryWrapper) {
        return mapper.selectList(queryWrapper);
    }


    /**
      * 分页获取列表信息
      * @param page
      * @param queryWrapper
      * @return
    */
    @Override
    public IPage<${entity}> selectPage(Page page, Wrapper<${entity}> queryWrapper) {
        return mapper.selectMapsPage(page,queryWrapper);
    }

    /**
      * 批量删除
      * @param ids
      * @return
    */
    @Override
    public int deleteBatchIds(Integer[] ids){
        return mapper.deleteBatchIds(Arrays.asList(ids));
    }
}
</#if>


mapper.java.ftl

package ${package.Mapper};

import ${package.Entity}.${entity};
import ${superMapperClassPackage};
import org.apache.ibatis.annotations.Mapper;

/**
 * <p>
 * ${table.comment!} Mapper 接口
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
@Mapper
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {

}
</#if>


entity.java.ftl

package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger2>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Data;
import lombok.EqualsAndHashCode;
    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

/**
 * <p>
 * ${table.comment!}
 * </p>
 *
 * @author ${author}
 * @since ${date}
 */
<#if entityLombokModel>
@Data
    <#if superEntityClass??>
@EqualsAndHashCode(callSuper = true)
    <#else>
@EqualsAndHashCode(callSuper = false)
    </#if>
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if table.convert>
@TableName("${table.name}")
</#if>
<#if swagger2>
@ApiModel(value="${entity}对象", description="${table.comment!}")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#else>
public class ${entity} implements Serializable {
</#if>

<#if entitySerialVersionUID>
    private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyName="${field.propertyName}"/>
    </#if>

    <#if field.comment!?length gt 0>
        <#if swagger2>
    @ApiModelProperty(value = "${field.comment}")
        <#else>
    /**
     * ${field.comment}
     */
        </#if>
    </#if>
    <#if field.keyFlag>
        <#-- 主键 -->
        <#if field.keyIdentityFlag>
    @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
        <#elseif idType??>
    @TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
        <#elseif field.convert>
    @TableId("${field.annotationColumnName}")
        </#if>
        <#-- 普通字段 -->
    <#elseif field.fill??>
    <#-- -----   存在字段填充设置   ----->
        <#if field.convert>
    @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
        <#else>
    @TableField(fill = FieldFill.${field.fill})
        </#if>
    <#elseif field.convert>
    @TableField("${field.annotationColumnName}")
    </#if>
    <#-- 乐观锁注解 -->
    <#if (versionFieldName!"") == field.name>
    @Version
    </#if>
    <#-- 逻辑删除注解 -->
    <#if (logicDeleteFieldName!"") == field.name>
    @TableLogic
    </#if>
    private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ---------->

<#if !entityLombokModel>
    <#list table.fields as field>
        <#if field.propertyType == "boolean">
            <#assign getprefix="is"/>
        <#else>
            <#assign getprefix="get"/>
        </#if>
    public ${field.propertyType} ${getprefix}${field.capitalName}() {
        return ${field.propertyName};
    }

    <#if chainModel>
    public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    <#else>
    public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {
    </#if>
        this.${field.propertyName} = ${field.propertyName};
        <#if chainModel>
        return this;
        </#if>
    }
    </#list>
</#if>

<#if entityColumnConstant>
    <#list table.fields as field>
    public static final String ${field.name?upper_case} = "${field.name}";

    </#list>
</#if>
<#if activeRecord>
    @Override
    protected Serializable pkVal() {
    <#if keyPropertyName??>
        return this.${keyPropertyName};
    <#else>
        return null;
    </#if>
    }

</#if>
<#if !entityLombokModel>
    @Override
    public String toString() {
        return "${entity}{" +
    <#list table.fields as field>
        <#if field_index==0>
            "${field.propertyName}=" + ${field.propertyName} +
        <#else>
            ", ${field.propertyName}=" + ${field.propertyName} +
        </#if>
    </#list>
        "}";
    }
</#if>
}

mapper.xml.ftl

<?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"/>

</#if>
<#if baseResultMap>
    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位-->
        <id column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
<#list table.commonFields as field><#--生成公共字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 -->
        <result column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
    </resultMap>

</#if>
<#if baseColumnList>
    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
<#list table.commonFields as field>
        ${field.columnName},
</#list>
        ${table.fieldNames}
    </sql>

</#if>
</mapper>

4.编写代码生成器代码

package com.example.mybatisgenerator.utils;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

/**
 * @author qzz
 */
public class CodeGenerateUtils {

    /**
     * 运行main方法进行代码生成
     * @param args
     */
    public static void main(String[] args) {
        //1.创建代码生成器
        AutoGenerator autoGenerator = new AutoGenerator();

        //2.全局配置
        GlobalConfig gc = new GlobalConfig();
        //String projectPath = System.getProperty("user.dir");
        //建议直接使用项目绝对路径,以防相对路径找不到出错
        gc.setOutputDir("D:\\generator" + "/src/main/java");
        //作者
        gc.setAuthor("admin");
        //生成后是否打开资源管理员
        gc.setOpen(false);
        //重新生成时文件是否覆盖
        gc.setFileOverride(true);
        //主键策略
        gc.setIdType(IdType.AUTO);
        //定义生成的实体类中日期类型
        gc.setDateType(DateType.ONLY_DATE);
        //开启swagger2模式(Swagger和项目整合用于请求测试)
        gc.setSwagger2(true);

        //自定义文件命名 注意 %s 会自动填充表实体属性
        gc.setControllerName("%sController");
        gc.setServiceName("%sService");
        gc.setServiceImplName("%sServiceImpl");
        gc.setMapperName("%sMapper");
        gc.setXmlName("%sMapper");

        autoGenerator.setGlobalConfig(gc);

        //3.数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/db_test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8");
        dsc.setDriverName("com.mysql.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);

        autoGenerator.setDataSource(dsc);

        //4.包配置
        PackageConfig pc = new PackageConfig();
        //模块名
        pc.setParent("com.example");
        pc.setModuleName("mybatisgenerator");
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setServiceImpl("service.impl");
        pc.setMapper("mapper");

        autoGenerator.setPackageInfo(pc);

        //5.策略配置
        StrategyConfig strategy = new StrategyConfig();
        //填写表名,帮助生成实体类等mapper相应代码
        strategy.setInclude("test_user");
        //数据库表映射到实体的命名策略
        strategy.setNaming(NamingStrategy.underline_to_camel);
        //生成实体时去掉表前缀
        strategy.setTablePrefix(pc.getModuleName()+"_");
        //数据库表字段映射到实体的命名策略
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        //lombok 模型   @Accessors(chain = true) setter链式操作
        strategy.setEntityLombokModel(true);
        //restful api 风格控制器
        strategy.setRestControllerStyle(true);
        //url 中驼峰转连字符
        strategy.setControllerMappingHyphenStyle(true);

        autoGenerator.setStrategy(strategy);

        //6.配置自定义模板
        //设置模板引擎    注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎
        autoGenerator.setTemplateEngine(new FreemarkerTemplateEngine());
        // 配置自定义输出模板
        //指定自定义模板路径, 位置:/resources/templates/entity.java.ftl(或者是.vm)
        // 注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setController("/templates/controller.java");
        templateConfig.setService("/templates/service.java");
        templateConfig.setServiceImpl("/templates/serviceImpl.java");
        templateConfig.setMapper("/templates/mapper.java");
        templateConfig.setEntity("/templates/entity.java");
        templateConfig.setXml("/templates/mapper.xml");
        autoGenerator.setTemplate(templateConfig);

        //6.执行
        autoGenerator.execute();
    }
}


5.测试运行

在这里插入图片描述

6.controller模板内的用到的工具类

响应数据:

package com.example.mybatisgenerator.utils;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;

/**
 * 响应数据
 * @author qzz
 */
@ApiModel("Result响应")
public class Result<T> implements Serializable {

    private static final long serialVersionUID=1L;

    /**
     * 编码 0:成功 其他值代表失败
     */
    @ApiModelProperty(value="编码 0:成功 其他值代表失败")
    private int code = 0;

    /**
     * 消息内容
     */
    @ApiModelProperty(value="消息内容")
    private String msg = "success";

    /**
     * 响应数据
     */
    @ApiModelProperty(value="响应数据")
    private T data;

    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;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Result<T> ok (T data){
        this.setData(data);
        return this;
    }

    public Result<T> error(){
        this.code = 500;
        this.msg="网络出错,请联系系统管理员";
        return this;
    }

    public Result<T> error(int code,String msg){
        this.code = code;
        this.msg=msg;
        return this;
    }

    public boolean success(){
        return code == 0 ? true : false;
    }
}

分页工具类:

package com.example.mybatisgenerator.utils;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * 分页工具类
 * @author qzz
 */
@Data
public class PageData<T> implements Serializable {

    private  static final long serialVersionUID = 1L;

    /**
     * 总记录数
     */
    private int total;
    /**
     * 列表数据
     */
    private List<T> list;

    public PageData(List<T> list, long total){
        this.list = list ;
        this.total = (int) total;
    }

}

三、代码源码

可点击此处进行下载