在最初的了解中,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 ${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<Coloum> 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/
欢迎转载、以及指出问题
网友评论