MybatisPlus3.5.12自定义代码生成器模板

MybatisPlus3.5.12自定义代码生成器模板

版本说明:

技术版本
mybatis-plus3.5.12
mybatis-plus-generator3.5.7

注意:此方案mybatis-plus-generator3.5.6以上版本的项目可用,如果你的项目中版本低于此版本,请先升级MybatisPlus版本

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.12</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-jsqlparser</artifactId>
    <version>3.5.12</version>

</dependency>
一 创建模板

模板代码都放在/resources/templates 目录下。例如/resources/templates/controller.java.ftl

controller.java.ftl
package ${package.Controller};

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import ${package.Entity}.${entity};
import ${package.Service}.${table.serviceName};
import com.pj.request.dto.LResponse;
import lombok.AllArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
<#list table.fields as field>
    <#if field.keyFlag>
        <#assign keyPropertyType="${field.propertyType}"/>
    </#if>
</#list>
/**
* <p>
    * ${table.comment} 前端控制器
    * </p>
*
* @author ${author}
* @since ${date}
*/
@RestController
@AllArgsConstructor
public class ${table.controllerName} {

    private final ${table.serviceName} service;

    @PostMapping("/${table.name}")
    public ResponseEntity<?> add(@RequestBody ${entity} entity) {
    service.save(entity);
        return ResponseEntity.ok(new LResponse(List.of(entity)));
    }

    @DeleteMapping("/${table.name}/{id}")
    public ResponseEntity<?> del(@PathVariable <#if table.havePrimaryKey>${keyPropertyType}<#else>Long</#if> id) {
        return ResponseEntity.ok(new LResponse(service.removeById(id)));
    }

    @PutMapping("/${table.name}")
    public ResponseEntity<?> update(@RequestBody ${entity} entity) {
        return ResponseEntity.ok(new LResponse(service.updateById(entity)));
    }

    @GetMapping("/${table.name}")
    public ResponseEntity<?> get(${entity} entity) {
        QueryWrapper<${entity}> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("created_at");
        return ResponseEntity.ok(new LResponse(service.list(wrapper)));
    }
}
entity.java.ftl
package ${package.Entity};

<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if springdoc>
import io.swagger.v3.oas.annotations.media.Schema;
<#elseif swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

    <#if chainModel>
import lombok.experimental.Accessors;
    </#if>
</#if>

/**
* <p>
    * ${table.comment!}
    * </p>
*
* @author ${author}
* @since ${date}
*/
<#if entityLombokModel>
@Data
@AllArgsConstructor
@NoArgsConstructor
    <#if chainModel>
@Accessors(chain = true)
    </#if>
</#if>
<#if table.convert>
@TableName("${schemaName}${table.name}")
</#if>
<#if springdoc>
@Schema(name = "${entity}", description = "${table.comment!}")
<#elseif swagger>
@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}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#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 springdoc>
            @Schema(description = "${field.comment}")
        <#elseif swagger>
            @ApiModelProperty("${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 field.versionField>
        @Version
    </#if>
<#-- 逻辑删除注解 -->
    <#if field.logicDeleteField>
        @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
    public 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>
}
二 配置生成策略
数据库配置信息
# application.yml
db:
  ip: localhost
  port: 3306
  username: root
  password: 123456
  server: life_space

对应的实体类

package com.pj.ls.code.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @author pj
 * @date 2025/4/1 21:11
 **/
@Component
@ConfigurationProperties(prefix = "db")
@AllArgsConstructor
@NoArgsConstructor
@Data
public class DatabasePropertity {
    private String ip;
    private String port;
    private String username;
    private String password;
    private String server;

}
代码生成器服务类

接口

package com.pj.ls.code.service;

import com.pj.ls.code.entity.CodeGeneratorParam;

public interface ICodeGenerator {
    boolean generate(CodeGeneratorParam param);
}

impl

package com.pj.ls.code.service.impl;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.config.rules.DbColumnType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.pj.ls.code.entity.CodeGeneratorParam;
import com.pj.ls.code.entity.DatabasePropertity;
import com.pj.ls.code.service.ICodeGenerator;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.sql.Types;
import java.util.Collections;

/**
 * @author pj
 * @date 2025/4/1 21:18
 **/
@Service
@AllArgsConstructor
public class CodeGeneratorImpl implements ICodeGenerator {
    private static final Logger logger = LoggerFactory.getLogger(CodeGeneratorImpl.class);

    private DatabasePropertity dbInfo;

    @Override
    public boolean generate(CodeGeneratorParam param) {
        try {
            String url =
                    "jdbc:mysql://" + dbInfo.getIp() + ":" + dbInfo.getPort() + "/" + dbInfo.getServer() +
                            "?useSSL=false&serverTimezone=UTC&remarks=true&useInformationSchema=true";
            FastAutoGenerator.create(url, dbInfo.getUsername(), dbInfo.getPassword())
                    .globalConfig(builder -> {
                        builder.author(param.getAuth()) // 设置作者
//                            .enableSwagger() // 开启 swagger 模式
                                .outputDir(param.getOutPutDir()); // 指定输出目录
                    })
                    .dataSourceConfig(builder ->
                            builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
                                int typeCode = metaInfo.getJdbcType().TYPE_CODE;
                                if (typeCode == Types.SMALLINT) {
                                    // 自定义类型转换
                                    return DbColumnType.INTEGER;
                                }
                                return typeRegistry.getColumnType(metaInfo);
                            })
                    )
                    .packageConfig(builder ->
                            builder.parent(param.getBasePath()) // 设置父包名
                                    .moduleName(param.getPackageName()) // 设置父包模块名
                                    .pathInfo(Collections.singletonMap(OutputFile.xml, param.getMapperPath())) // 设置mapperXml生成路径
                    )
                    .strategyConfig(builder ->
                                    builder.addInclude(param.getTableName()) // 设置需要生成的表名
                                            .controllerBuilder().template("/templates/controller.java").enableFileOverride()
                                            .entityBuilder().javaTemplate("templates/entity.java").enableLombok().enableFileOverride()
                                            .serviceBuilder().enableFileOverride()
                                            .mapperBuilder().enableFileOverride()
//                                .addTablePrefix("t_", "c_") // 设置过滤表前缀
                    )
                    .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                    .execute();
            return true;
        } catch (RuntimeException e) {
            logger.error("代码生成出错", e);
            return false;

        }
    }
}
三 调用代码生成器
controller层
package com.pj.ls.code.controller;

import cn.hutool.core.util.StrUtil;
import com.pj.ls.code.entity.CodeGeneratorParam;
import com.pj.ls.code.service.ICodeGenerator;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author pj
 * @date 2025/4/1 21:07
 **/
@RestController
@AllArgsConstructor
public class GeneratorController {
    private ICodeGenerator service;

    @PostMapping("/generate")
    public boolean generate(@RequestBody CodeGeneratorParam params) {
        if (StrUtil.isEmpty(params.getAuth())
        || StrUtil.isEmpty(params.getBasePath())
        || StrUtil.isEmpty(params.getMapperPath())
        || StrUtil.isEmpty(params.getOutPutDir())
        || StrUtil.isEmpty(params.getPackageName())
        || StrUtil.isEmpty(params.getTableName())
        ){
            return false;
        }
        return service.generate(params);
    }

}
前端 vue 页面
<template>
  <div
    style="
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
    "
  >
    <div class="tech-background">
      <!--            <div style="width: 100%;position: absolute;right: -20px;top: 20px;" >-->
      <!--                <i class="el-icon-s-home"></i>-->
      <!--                <el-button type="text" size="default" @click="back">首页</el-button>-->

      <!--            </div>-->
      <div class="title">
        <div>代码生成器</div>
      </div>

      <form @submit.prevent="generateCode(form)" class="tech-form">
        <div class="form-row">
          <div class="form-group">
            <label for="auth">作者</label>
            <input
              required
              type="text"
              id="auth"
              placeholder="请输入代码作者"
              v-model="form.auth"
            />
          </div>
          <div class="form-group">
            <label for="outPutDir">文件生成所在目录(项目根目录)</label>
            <input
              required
              type="text"
              id="outPutDir"
              v-model="form.outPutDir"
              placeholder="例:F:\project\java\demo_20\module_01\src\main\java"
            />
          </div>
        </div>
        <div class="form-row">
          <div class="form-group">
            <label for="basePath">父包路径</label>
            <input
              required
              type="text"
              id="basePath"
              placeholder="例:com.pj.ls"
              v-model="form.basePath"
            />
          </div>
          <div class="form-group">
            <label for="packageName">包名</label>
            <input
              required
              type="text"
              id="packageName"
              placeholder="例:bill"
              v-model="form.packageName"
            />
          </div>
        </div>
        <div class="form-row">
          <div class="form-group">
            <label for="mapperPath">mapper文件路径</label>
            <input
              required
              type="text"
              id="mapperPath"
              v-model="form.mapperPath"
              placeholder="例:F:\project\java\demo_20\module_01\src\main\resources\com\pj\bill\mapper"
            />
          </div>
          <div class="form-group">
            <label for="tableName">表名</label>
            <input
              required
              type="text"
              id="tableName"
              v-model="form.tableName"
              placeholder="例:sys_user"
            />
          </div>
        </div>
        <button type="submit" class="btn-submit">一键生成</button>
        <div
          style="
            color: #ffadad;
            border: none;
            background-color: rgba(138, 43, 226, 0.82);
            width: fit-content;
            border-radius: 5px;
            padding: 5px;
            float: right;
          "
        >
          注意:在同一路径下对同一张表重复生成代码,原代码会被新生成的代码覆盖!
        </div>
      </form>
    </div>
  </div>
</template>

<script>
import { generate } from '@/api/codeRequest'
import { mapMutations, mapState } from 'vuex'

export default {
  data() {
    return {
      form: {
        auth: '',
        outPutDir: '',
        basePath: '',
        packageName: '',
        mapperPath: '',
        tableName: '',
      },
    }
  },
  methods: {
    ...mapMutations('app', ['UPDATE_SHOW_FLAG']),

    generateCode(params) {
      generate(params)
        .then((res) => {
          if (res) {
            this.$message({ message: '生成成功', type: 'success' })
          } else {
            this.$message({ message: '生成失败', type: 'error' })
          }
        })
        .catch((err) =>
          this.$message({ message: '生成失败:' + err, type: 'error' })
        )
    },
    back() {
      this.UPDATE_SHOW_FLAG(true)
      this.$router.push('/')
    },
  },
  computed: {
    ...mapState('app', ['showIndexPanel']),
  },
  mounted() {
    // this.UPDATE_SHOW_FLAG(false);
  },
  beforeRouteEnter(to, from, next) {
    next()
  },
}
</script>

<style scoped>
.tech-background {
  position: relative;
  width: 95%;
  height: 95%;
  background: linear-gradient(135deg, #00ffcc, #0066ff);
  border-radius: 20px;
  box-shadow: 0 0 50px rgba(0, 255, 204, 0.5), 0 0 20px rgba(0, 102, 255, 0.5);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: white;
}
.tech-background::before,
.tech-background::after {
  content: '';
  position: absolute;
  width: 200px;
  height: 200px;
  background: radial-gradient(circle, rgba(0, 255, 204, 0.8), transparent 70%);
  border-radius: 50%;
  animation: move 10s linear infinite;
}
.tech-background::before {
  top: -50px;
  left: -50px;
  animation-delay: 0s;
}
.tech-background::after {
  bottom: -50px;
  right: -50px;
  animation-delay: 5s;
}
@keyframes move {
  0% {
    transform: translate(0, 0);
  }
  50% {
    transform: translate(100px, 100px);
  }
  100% {
    transform: translate(0, 0);
  }
}
.tech-background .glow {
  position: absolute;
  width: 100%;
  height: 100%;
  background: radial-gradient(circle, rgba(0, 255, 204, 0.3), transparent 70%);
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  animation: glow 3s ease-in-out infinite alternate;
}
@keyframes glow {
  0% {
    opacity: 0.5;
  }
  100% {
    opacity: 1;
  }
}
.title {
  display: flex;
  margin-top: -50px;
  margin-bottom: 100px;
  justify-content: center;
  align-items: normal;
  font-size: 300%;
  font-weight: bold;
  opacity: 70%;
  color: #0056d4;
  width: 600px;
  box-shadow: 2px 5px 10px 2px rgba(142, 21, 168, 0.81);
}
.tech-form {
  width: 80%;
  display: flex;
  flex-direction: column;
  gap: 20px;
  z-index: 1;
}
.form-row {
  display: flex;
  gap: 20px;
}
.form-group {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.form-group label {
  font-size: 16px;
  font-weight: bold;
  color: rgba(165, 3, 234, 0.62);
}
.form-group input {
  padding: 10px;
  border: 2px solid #00ffcc;
  border-radius: 10px;
  background: rgba(109, 213, 126, 0.3);
  color: #ffffff;
  font-size: 14px;
  outline: none;
  transition: all 0.3s ease;
}
.form-group input:focus {
  border-color: #0066ff;
  box-shadow: 0 0 10px rgba(0, 102, 255, 0.5);
}
.btn-submit {
  padding: 12px;
  border: none;
  border-radius: 10px;
  background: linear-gradient(135deg, #00ffcc, rgba(200, 0, 255, 0.52));
  color: white;
  font-size: 16px;
  font-weight: bold;
  cursor: pointer;
  transition: all 0.3s ease;
}
.btn-submit:hover {
  box-shadow: 0 0 15px rgba(233, 238, 238, 0.5);
}
</style>

请求接口

import axios from 'axios'

export function generate(data) {
  return axios.post('generate',data)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值