自定义MBG:Mybatis-Generator使用FreeMarker进行代码生成

tech2025-05-23  16

在最初的了解中,mybatis官方是有MBG的,但网上一直说不好用。于是网络上有各种开源MBG,各有优劣。基本上难以满足一些自定义的需求。

在百度摸索几天后,我决定自定义一个符合自己项目的generator,使用FreeMarker模板引擎,生成实体,mapper接口,MapperXml,Service,ServiceImpl,以及Controller

代码如下 思路:建立一个工具类执行入口,指定要生成实体的数据源、生成位置等信息

package com.kichun.ucenter;

import com.kichun.common.util.FreeMarkerGeneratorUtil; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**

代码生成器配置

Created by wangqichang on 2018/5/31. / @SpringBootTest @RunWith(SpringJUnit4ClassRunner.class) @Slf4j public class CodeGenerator { /*

数据库驱动未提供注入时请自行提供驱动 / @Value("${druid.primary.driverClassName}") private String driverClassName; /*数据库URL / @Value("${druid.primary.url}") private String url; /*用户名 */ @Value("${druid.primary.username}") private String username;

/**

密码 */ @Value("${druid.primary.password}") private String password;

/**

数据库名 */ private String databaseName = “kichun_dev”;

/**

只生成单表,非必须 */ private String tableName = “”;

/**

表前缀,非必须 */ private String tablePrefix = “t_”;

/**

生成级别 必须1 仅生成dao层2 生成service层3 生成controller层 */ private int genenaterLevel = 1;

/**

基本包名 必须 */ private String basePackage = “com.kichun.ucenter.entity”;

/**

mapper接口包名 无则使用基本包名 */ private String mapperPackage = “”;

/**

mapper.xml 生成路径 无则使用基本包名 */ private String xmlDir = “”;

/**

servcie包名 impl也在此包路径下 无则使用基本包名 */ private String servicePackage = “”;

/**

controller包名 无则使用基本包名 */ private String controllerPackage = “”;

@Test public void freeMarkerTest() { FreeMarkerGeneratorUtil.generatorMvcCode( driverClassName, url, username, password, tableName, databaseName, tablePrefix, genenaterLevel, basePackage, mapperPackage, xmlDir, servicePackage, controllerPackage); }

}

第二,定义工具类,根据提供的配置查询数据库,遍历表,根据对应模板文件生成文件到指定输出目录(可以根据包名生成对应目录)

ps:仅完成了实体生成,其他文件因为未准备模板没有时间继续搞了,但思路是ok的

package com.kichun.common.util;

import cn.hutool.core.io.FileUtil; import cn.hutool.core.util.StrUtil; import com.kichun.common.vo.Coloum; import com.kichun.common.vo.EntityDataModel; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; import lombok.extern.slf4j.Slf4j;

import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.ArrayList; import java.util.Date; import java.util.List;

/**

代码生成工具类

Created by wangqichang on 2018/5/30. */ @Slf4j public class FreeMarkerGeneratorUtil {

/**

生成三层代码 包含 仅生成dao层 (包含实体Entity及mapper接口及xml) 生成service层 (包含service接口及impl) 生成controller层

@param driver

@param url

@param user

@param pwd */ public static void generatorMvcCode(String driver, String url, String user, String pwd, String tableName, String databaseName, String tablePrefix, int generateLevel, String basePackage, String mapperPackage, String xmlDir, String servicePackage, String controllerPackage) {

Connection con = null; //注册驱动 try { Class.forName(driver); con = DriverManager.getConnection(url, user, pwd); } catch (Exception e) { log.error(“获取数据连接失败,{}”, e.getMessage()); return; }

//查询dbName所有表 String sql = “select table_name from information_schema.tables where table_schema=’” + databaseName + “’”;

//获取当前项目路径 String path = FreeMarkerGeneratorUtil.class.getResource("/").getPath(); path = StrUtil.sub(path, 1, path.indexOf("/target"));

log.info(“当前项目路径为:{}”, path); String parentProjectPath = StrUtil.sub(path, 0, path.lastIndexOf("/")); //获取模板路径 String templatePath = path + “/src/main/resources/template”; log.info(“当前模板路径为:{}”, templatePath);

boolean onlySingleTable = StrUtil.isNotBlank(tableName); try {

PreparedStatement ps = con.prepareStatement(sql); ResultSet rs = ps.executeQuery(); while (rs.next()) { if (!onlySingleTable) { tableName = rs.getString(1); } String entityDir = null; //根据实体包名创建目录 if (generateLevel > 0) { File[] ls = FileUtil.ls(parentProjectPath); for (File f: ls) { String currModule = f.toString(); boolean matches = currModule.matches("(.*?pojo.*?)|(.*?domain.*?)|(.*?entity.*?)"); if (f.isDirectory()&&matches){ entityDir = f.toString()+ "/src/main/java/" + basePackage.replace(".", "/"); break; } } if (StrUtil.isBlank(entityDir)){ entityDir = path + "/src/main/java/" + basePackage.replace(".", "/"); } if (!FileUtil.exist(entityDir)) { FileUtil.mkdir(entityDir); log.info("创建目录:{} 成功! ",entityDir); } } EntityDataModel entityModel = getEntityModel(con, tableName, basePackage, tablePrefix); //生成每个表实体 generateCode(entityModel, templatePath, "Entity.ftl", entityDir); //创建mapperxml路径 /*String mapperxmlPath = null; //根据实体包名创建目录 if (StrUtil.isNotBlank(mapperPackage)) { mapperxmlPath = path + mapperPackage.replace(".", "/"); if (!FileUtil.exist(mapperxmlPath)) { FileUtil.mkdir(mapperxmlPath); } } else { mapperxmlPath = entityDir; }*/ //generateCode(MapperXmlDataModel,templatePath,"MapperXml.ftl",mapperxmlPath); //创建service包名 //创建serviceImpl包名 //创建controller包名 //生成mapper //生成xml //生成service //生成impl //生成controller if (onlySingleTable) { return; } }

} catch (Exception e) { log.error(“代码生成出错 {}”, e.getMessage()); }

}

private static EntityDataModel getEntityModel(Connection con, String tableName, String basePackage, String tablePrefix) throws Exception { //查询表属性,格式化生成实体所需属性 String sql = "SELECT table_name, column_name, column_comment, column_type, column_key, column_default " + "FROM INFORMATION_SCHEMA. COLUMNS " + “WHERE table_name = '” + tableName + "’ " + “AND table_schema = ‘kichun_dev’”;

PreparedStatement ps = con.prepareStatement(sql); ResultSet rs = ps.executeQuery(); List<Coloum> columns = new ArrayList<>(); while (rs.next()) { Coloum col = new Coloum(); String name = rs.getString("column_name"); String type = rs.getString("column_type"); String comment = rs.getString("column_comment"); String annotation = null; if ("id".equals(name)) { if (type.contains("int")) { annotation = "@TableId(type = IdType.AUTO)"; } } if (type.contains("varchar")) { type = "String"; } else if (type.contains("datetime")) { type = "Date"; } else if (type.contains("int")) { type = "Integer"; } else { type = "String"; } col.setName(StrUtil.toCamelCase(name)); col.setType(type); col.setAnnotation(annotation); col.setComment(comment); columns.add(col); } EntityDataModel dataModel = new EntityDataModel(); dataModel.setEntityPackage(basePackage); dataModel.setCreateTime(new Date().toString()); if (StrUtil.isNotBlank(tablePrefix)) { dataModel.setEntityName(StrUtil.upperFirst(StrUtil.toCamelCase(StrUtil.removePrefix(tableName, tablePrefix)))); } else { dataModel.setEntityName(StrUtil.upperFirst(StrUtil.toCamelCase(tableName))); } dataModel.setTableName(tableName); dataModel.setColumns(columns); return dataModel;

}

private static void generateCode(EntityDataModel dataModel, String templatePath, String templateName, String outDir) throws IOException, TemplateException {

String file = outDir +"/"+ dataModel.getEntityName() + dataModel.getFileSuffix(); if (FileUtil.exist(file)){ log.info("文件:{} 已存在,如需覆盖请先对该文件进行"); return; } //获取模板对象 Configuration conf = new Configuration(); File temp = new File(templatePath); conf.setDirectoryForTemplateLoading(temp); Template template = conf.getTemplate(templateName); Writer writer = new FileWriter(file); //填充数据模型 template.process(dataModel, writer); writer.close(); log.info("代码生成成功,文件位置:{}",file);

} }

实体模板Entity.ftl参考

ps :可以使用其他模板引擎进行生成,注意需要引入相关引擎依赖

package ${entityPackage};

import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import lombok.Data;

import java.io.Serializable; import java.util.Date;

/**

${entityName} 实体类

Created by ${author} on c r e a t e T i m e . ∗ / @ D a t a @ T a b l e N a m e ( " {createTime}. */ @Data @TableName(" createTime./@Data@TableName("{tableName}") public class ${entityName} implements Serializable{ <#list columns as column>

/**

${(column.comment)!} */ ${(column.annotation)!} private ${column.type} ${column.name}; </#list> }

数据模型参考

package com.kichun.common.vo;

import lombok.Data;

import java.util.Date; import java.util.List;

/**

模板生成属性

Created by wangqichang on 2018/5/30. / @Data public class EntityDataModel { /*

base package / private String entityPackage; /*文件名后缀 */ private String fileSuffix = “.java”;

/**

实体名 */ private String entityName;

/**

作者 默认 */ private String author=“auto generator”;

/**

创建时间 */ private String createTime = new Date().toString();

/**

表名 */ private String tableName;

/**

字段集合 */ private List columns;

}

字段模型

package com.kichun.common.vo;

import lombok.Data;

import java.io.Serializable;

/**

代码生成列实体

Created by wangqichang on 2018/5/30. / @Data public class Coloum implements Serializable{ /*

属性注解 */ private String annotation;

/**

属性名 */ private String name;

/**

属性类型 */ private String type;

/**

属性注释 */ private String comment; }

生成的实体java文件

import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import lombok.Data;

import java.io.Serializable; import java.util.Date;

/**

User 实体类

Created by auto generator on Fri Jun 01 15:09:37 CST 2018. */ @Data @TableName(“t_user”) public class User implements Serializable{

/** * */ @TableId(type = IdType.AUTO) private Integer id;

/** * */

private String name;

/** * */

private String pwd;

/** * */

private String realName;

/** * */

private Date createTime;

/** * */

private String createId; }

其他:

引入相关依赖(freeMarker) 使用了开源工具类hutool 适用于需自定义的MBG 然而,我发现了更齐全的MBG:mybatis-plus所带的生成器,更适合使用了mybatis-plus工具的代码生成,有兴趣可以了解下http://mp.baomidou.com/欢迎转载、以及指出问题作者:老王_KICHUN链接:https://www.jianshu.com/p/0a5f8dba9b1d来源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

最新回复(0)