MybatisPlusGenerator自定义模板生成文件
本文章按照MybatisPlusGenerator规范,并自定义模版生成对应的Controller,IService,ServiceImpl,Entity,Mapper,Mapper.xml
依赖包引入
<!--数据库驱动 可以根据需要自行调整-->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1</version>
</dependency>
<!--Mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis-plus逆向工程-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!--freemarker-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--Velocity引擎模板-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity</artifactId>
<version>1.7</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
实现类源码
package com.example.mybatisdemo;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
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.fill.Column;
import com.baomidou.mybatisplus.generator.fill.Property;
import java.util.Collections;
import java.util.Scanner;
/**
* @author : Doug Liang
* @date : 2024/8/23 14:11
* @version: 1.0
* @description:
*/
public class MybatisPlusGeneratorNew {
protected static String URL = "jdbc:oracle:thin:@192.168.137.12:1521:yscrmora";
protected static String USERNAME = "yscrmora";
protected static String PASSWORD = "oracle";
protected static DataSourceConfig DATA_SOURCE_CONFIG = new DataSourceConfig.Builder(URL, USERNAME,
PASSWORD).build();
public static void main(String[] args) {
System.out.println("请输入表名 , 多个英文逗号分隔");
String[] tableNames = new Scanner(System.in).next().split(",");
for (String tableName : tableNames) {
AutoGenerator generator = new AutoGenerator(DATA_SOURCE_CONFIG);
// 全局配置
GlobalConfig globalConfig = new GlobalConfig.Builder()
.fileOverride() // 如果文件存在,是否覆盖
.outputDir(System.getProperty("user.dir") + "/src/main/java") // 生成文件的输出目录
// .enableSwagger() // 是否开启Swagger2
.commentDate("yyyy-MM-dd") // 注释中的日期格式
.dateType(DateType.TIME_PACK) // 时间类型,默认使用java.util.Date
.author("DougLiang")
.build();
generator.global(globalConfig);
// 包配置(PackageConfig)
PackageConfig packageConfig = new PackageConfig.Builder()
.parent("com.tlit.mybatis")
.entity("entity")
.service("service")
.serviceImpl("service.Impl")
.controller("controller")
.mapper("mapper")
.xml("mapper.xml")
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir") + "\\src\\main\\resources\\mapper"))
.build();
generator.packageInfo(packageConfig);
// 模板配置(TemplateConfig)
TemplateConfig templateConfig = new TemplateConfig.Builder()
.disable(TemplateType.ENTITY) // 禁用实体类的默认模板
.entity("/templates/entity.java") // 自定义实体类模板文件路径
.service("/templates/service.java") // 自定义服务接口模板文件路径
.serviceImpl("/templates/serviceImpl.java") // 自定义服务实现类模板文件路径
.mapper("/templates/mapper.java") // 自定义Mapper接口模板文件路径
.mapperXml("/templates/mapper.xml") // 自定义XML映射文件模板文件路径
.controller("/templates/controller.java") // 自定义控制器类模板文件路径
.build();
generator.template(templateConfig);
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig.Builder()
.enableCapitalMode() // 开启大写命名
.enableSkipView() // 是否跳过视图
.disableSqlFilter() // 禁用 SQL 过滤器
.addInclude(tableName) // 需要包含的表名,使用正则表达式
.build();
// 实体配置
strategyConfig.entityBuilder()
// .disableSerialVersionUID() // 禁用 serialVersionUID
.enableChainModel() // 开启链式模型
.enableLombok() // 开启Lombok
.enableRemoveIsPrefix() // 移除字段名中的is前缀
.enableTableFieldAnnotation() // 开启字段注解
.enableActiveRecord() // 开启ActiveRecord模式
.versionColumnName("version") // 乐观锁字段名
.versionPropertyName("version") // 乐观锁属性名
.logicDeleteColumnName("deleted") // 逻辑删除字段名
.logicDeletePropertyName("deleted") // 逻辑删除属性名
// .naming(NamingStrategy.underline_to_camel) // 命名策略:不改变原始表名
// .columnNaming(NamingStrategy.underline_to_camel) // 字段命名策略:下划线转驼峰
.addTableFills(new Column("create_time", FieldFill.INSERT)) // 添加表填充字段
.addTableFills(new Property("update_time", FieldFill.INSERT_UPDATE)) // 添加表填充属性
// .idType(IdType.AUTO) // 主键类型:自增
// .formatFileName(formatTableName) // 文件名格式化
.build();
// 根据名字取选择是否要格式化表名 我这里表名是XW_*
String formatTableName = toCamelCase(tableName.substring(3));
// 控制器配置
strategyConfig.controllerBuilder()
.enableRestStyle() // 开启RestController风格
.formatFileName(formatTableName + "Controller") // 控制器文件名格式化
.build();
// 服务配置
strategyConfig.serviceBuilder()
.formatServiceFileName("I" + formatTableName + "Service") // 服务接口文件名格式化
.formatServiceImplFileName(formatTableName + "ServiceImpl") // 服务实现类文件名格式化
.superServiceImplClass("I" + formatTableName + "Service") // 设置父类为空,即不继承任何父类
.build();
tableName = toCamelCase(tableName);
// Mapper配置
strategyConfig.mapperBuilder()
.superClass(BaseMapper.class) // Mapper接口的父类
.enableMapperAnnotation() // 开启Mapper注解
.enableBaseResultMap() // 开启基本结果映射
.enableBaseColumnList() // 开启基本列映射
.formatMapperFileName(tableName + "Mapper") // Mapper接口文件名格式化
.formatXmlFileName(tableName + "Mapper"); // XML映射文件名格式化
generator.strategy(strategyConfig);
generator.execute();
}
}
public static String toCamelCase(String s) {
StringBuilder camelCaseStr = new StringBuilder();
String[] parts = s.split("_");
for (String part : parts) {
camelCaseStr.append(toProperCase(part));
}
return camelCaseStr.toString();
}
public static String toProperCase(String s) {
return s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
}
}
模版文件
根据上面的源码我们定义的模版文件实在resource/templates/
文件夹下,此时只会读取我们定义的文件模版,模板文件格式我这里使用的是vm格式的,默认会根据VelocityTemplateEngine读取,无需自己再添加后缀的格式,所以在上面的源码中定义读取的模版文件里,并没有将文件以.vm结尾。对应读取的源码也给大家看一下,无需大家再写。
public @NotNull String templateFilePath(@NotNull String filePath) {
String dotVm = ".vm";
return filePath.endsWith(".vm") ? filePath : filePath + ".vm";
}
下面是我根据项目需要做的一些自定义模板,供大家参考使用
文件名:entity.java.vm
package ${package.Entity};
#foreach($pkg in ${table.importPackages})
import ${pkg};
#end
#if(${entityLombokModel})
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#if(${chainModel})
import lombok.experimental.Accessors;
#end
#end
/**
* <p>
* $!{table.comment} ${entity}实体类
* </p>
* @author ${author}
* @since ${date}
*/
#if(${entityLombokModel})
@SuppressWarnings("all")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
#end
#if(${table.convert})
@TableName("${table.name}")
#end
#if(${swagger2})
@ApiModel(value="${entity}对象", description="$!{table.comment}")
#end
#if(${superEntityClass})
public class ${entity} extends ${superEntityClass}#if(${activeRecord})<${entity}>#end implements Serializable {
#elseif(${activeRecord})
public class ${entity} extends Model<${entity}> implements Serializable {
#else
public class ${entity} implements Serializable {
#end
#if(${entitySerialVersionUID})
private static final long serialVersionUID = 1L;
#end
## ---------- 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.annotationColumnName}", type = IdType.AUTO)
#elseif(!$null.isNull(${idType}) && "$!idType" != "")
@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
#elseif(${field.convert})
@TableId("${field.annotationColumnName}")
#end
## 普通字段
#elseif(${field.fill})
## ----- 存在字段填充设置 -----
#if(${field.convert})
@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
#else
@TableField(fill = FieldFill.${field.fill})
#end
#elseif(${field.convert})
@TableField("${field.annotationColumnName}")
#end
## 乐观锁注解
#if(${versionFieldName}==${field.name})
@Version
#end
## 逻辑删除注解
#if(${logicDeleteFieldName}==${field.name})
@TableLogic
#end
private ${field.propertyType} ${field.propertyName};
#end
## ---------- END 字段循环遍历 ----------
#if(!${entityLombokModel})
#foreach($field in ${table.fields})
#if(${field.propertyType.equals("boolean")})
#set($getprefix="is")
#else
#set($getprefix="get")
#end
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}) {
#end
this.${field.propertyName} = ${field.propertyName};
#if(${chainModel})
return this;
#end
}
#end
## --foreach end---
#end
## --end of #if(!${entityLombokModel})--
#if(${entityColumnConstant})
#foreach($field in ${table.fields})
public static final String ${field.name.toUpperCase()} = "${field.name}";
#end
#end
#if(!${entityLombokModel})
@Override
public String toString() {
return "${entity}{" +
#foreach($field in ${table.fields})
#if($!{foreach.index}==0)
"${field.propertyName}=" + ${field.propertyName} +
#else
", ${field.propertyName}=" + ${field.propertyName} +
#end
#end
"}";
}
#end
/**
* 当前页码
*/
@TableField(exist = false)
private long pageNo;
/**
* 每页大小
*/
@TableField(exist = false)
private long pageSize;
}
文件名:controller.java.vm
package ${package.Controller};
import ${package.Service}.${table.serviceName};
import ${package.Entity}.${entity};
import com.tlcb.pf.web.util.PageView;
import com.tlit.platform.core.ViewResult;
import com.tlit.platform.constdef.ConstDef;
import org.apache.ibatis.session.RowBounds;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* <p>
* $!{table.comment} 前端控制器
* </p>
* @author ${author}
* @date ${date}
* @since 1.0.0
*/
@SuppressWarnings("all")
@Controller
@RequestMapping("/app/view/${table.entityPath}")
public class ${table.controllerName} {
private static final Logger logger = LoggerFactory.getLogger(${table.controllerName}.class);
@Autowired
private ${table.serviceName} ${table.entityPath}Service;
@RequestMapping("/insert")
@ResponseBody
@Transactional
public ViewResult insert(@RequestBody ${entity} ${table.entityPath}, HttpServletRequest request) {
ViewResult result = new ViewResult();
${table.entityPath}Service.insert(${table.entityPath});
result.setResult(ConstDef.ViewResult.SUCCESS);
return result;
}
@RequestMapping("/delete")
@ResponseBody
@Transactional
public ViewResult delete(@RequestParam("id") String id, HttpServletRequest request) {
ViewResult result = new ViewResult();
// TODO: 请填入业务逻辑
${table.entityPath}Service.delete(id);
result.setResult(ConstDef.ViewResult.SUCCESS);
return result;
}
@RequestMapping("/update")
@ResponseBody
@Transactional
public ViewResult update(@RequestBody ${entity} ${table.entityPath}, HttpServletRequest request) {
ViewResult result = new ViewResult();
xwCustomerService.update(${table.entityPath});
result.setResult(ConstDef.ViewResult.SUCCESS);
return result;
}
@GetMapping("/list")
public PageView list(${entity} ${table.entityPath}, HttpServletRequest request,
@RequestParam(defaultValue = "1") String pageNow,
@RequestParam(defaultValue = "10") String pageSize) {
int pageNowInt = Integer.parseInt(pageNow);
int pageSizeInt = Integer.parseInt(pageSize);
// 创建查询对象
int offset = (pageNowInt - 1) * pageSizeInt;
int limit = pageNowInt * pageSizeInt;
RowBounds rowBounds = new RowBounds(offset, limit);
List<${entity}> data = ${table.entityPath}Service.selectByRowBounds(${table.entityPath},rowBounds);
Long count = ${table.entityPath}Service.count(${table.entityPath});
PageView pageView = new PageView(pageNowInt);
pageView.setRecords(data);
pageView.setRowCountByPageSize(count, pageSizeInt);
return pageView;
}
}
文件名:service.java.vm
package ${package.Service};
import ${package.Entity}.${entity};
import org.apache.ibatis.session.RowBounds;
import java.util.List;
/**
* <p>
* $!{table.comment} 服务类
* </p>
* @author ${author}
* @since ${date}
*/
public interface ${table.serviceName} {
/**
* 向$!{table.comment}添加一条数据
* @param ${table.entityPath} 参数
* @return 影响行数
*/
int insert(${entity} ${table.entityPath});
/**
* 删除$!{table.comment}中一条数据
* @param id 参数
* @return 影响行数
*/
int delete(String id);
/**
* 修改$!{table.comment}中一条数据信息
* @param ${table.entityPath} 参数
* @return 影响行数
*/
int update(${entity} ${table.entityPath});
/**
*
* @param ${table.entityPath} 参数
* @param rowBounds 分页条数对象
* @return
*/
List<${entity}> selectByRowBounds(${entity} ${table.entityPath}, RowBounds rowBounds);
/**
* @param ${table.entityPath} 参数
* @return 条数
*/
Long count(${entity} ${table.entityPath});
}
文件名:serviceImpl.java.vm
package ${package.ServiceImpl};
import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${package.Service}.${table.serviceName};
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Autowired;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.session.RowBounds;
import java.util.List;
/**
* <p>
* $!{table.comment} 服务实现类
* </p>
* @author ${author}
* @since ${date}
*/
@Service
public class ${table.serviceImplName} implements ${table.serviceName} {
private static final Logger logger = LoggerFactory.getLogger(${table.serviceImplName}.class);
@Autowired
private ${table.mapperName} ${table.entityPath}Mapper;
@Override
public int insert(${entity} ${table.entityPath}) {
return ${table.entityPath}Mapper.insert(${table.entityPath});
}
@Override
public int delete(String id) {
// 按主键删除
return ${table.entityPath}Mapper.deleteById(id);
}
@Override
public int update(${entity} ${table.entityPath}) {
LambdaUpdateWrapper<${entity}> wrapper = new LambdaUpdateWrapper<>();
// TODO: 条件请自己处理
return ${table.entityPath}Mapper.update(${table.entityPath}, wrapper);
}
@Override
public List<${entity}> selectByRowBounds(${entity} ${table.entityPath},RowBounds rowBounds){
// TODO: 条件请自己处理
return ${table.entityPath}Mapper.selectByCase(rowBounds.getOffset(), rowBounds.getLimit());
}
@Override
public Long count(${entity} ${table.entityPath}){
return ${table.entityPath}Mapper.count(${table.entityPath});
}
}
文件名:mapper.java.vm
package ${package.Mapper};
import ${package.Entity}.${entity};
import ${superMapperClassPackage};
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* $!{table.comment} Mapper 接口
* </p>
* @author ${author}
* @since ${date}
*/
@SuppressWarnings("all")
@Mapper
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {
List<${entity}> selectByCase(@Param("start")int start, @Param("limit")int limit);
Long count(${entity} ${table.entityPath});
}
文件名: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
<select id="count" resultType="long">
SELECT COUNT (*) FROM ${table.name}
<include refid="selectPageClause"/>
</select>
<select id="selectByCase" resultMap="BaseResultMap">
select * from (select t.*,rownum r from (
SELECT * FROM ${table.name}
<include refid="selectPageClause"/>
) t
where rownum<![CDATA[<]]>=#{limit}) where r<![CDATA[>]]>#{start}
</select>
<sql id="selectPageClause">
<where>
-- TODO:查询条件请自己添加
</where>
</sql>
</mapper>
因为项目需要,生成的mapper.xml
中自带了分页逻辑
综上,自定义模板生成完成。实际上还可以用别的模版去做,但是个人觉得使用VelocityTemplateEngine读取时,模版更好处理一点。但是切记不要在模版中直接使用格式化。