概述
在使用mybatis时,手动书写mapper.xml工作量比较大且容易出错,所以项目一般通过Mybatis-generator 工具自动为项目成成 Mapping映射文件。同样Mybatis-generator 也需要一个配置文件 用于定义 mapper类及xml文件的相关信息。而在微服务架构下,一般将整个项目拆成多个微服务服务模块,每个服务模块都需要生成自己相关业务表的mapper文件,所以每个模块都需要相应的generator配置文件,这些配置文件在一个项目中配置都大体相同。为了不重复为每个微服务模块书写mybatis-generator配置文件,这里提供一种相对通用的可配置化的生成方案。
项目的相关约定
数据库表名需要带有模块前缀,比如 对于用户中心模块,数据库表名统一以 ucenter_为前缀。如果当前项目不符合规范的,可以自行通过修改工具类来支持。
生成
首先通过数据库配置信息连接数据库,之后通过模块名查询获取到数据库中以该模块名为前缀的所有表。之后将这些相关配置信息传入Velocity模板生成文件。
代码
package com.zheng.common.util;
import org.apache.commons.lang.ObjectUtils;
import org.apache.velocity.VelocityContext;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import static com.zheng.common.util.StringUtil.lineToHump;
/**
* 代码生成类
*/
public class MybatisGeneratorUtil {
package com.knight.common.util;
import org.apache.commons.lang.ObjectUtils;
import org.apache.velocity.VelocityContext;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 代码生成类
*/
public class MybatisGeneratorUtil {
// generatorConfig模板路径
private static String generatorConfig_vm = "/template/generatorConfig.vm";
/**
* 根据模板生成generatorConfig.xml文件
* @param jdbc_driver 驱动路径
* @param jdbc_url 链接
* @param jdbc_username 帐号
* @param jdbc_password 密码
* @param module 项目模块
* @param database 数据库
* @param table_prefix 表前缀
* @param package_name 包名
*/
public static void generator(
String jdbc_driver,
String jdbc_url,
String jdbc_username,
String jdbc_password,
String module,
String database,
String table_prefix,
String package_name,
Map<String, String> last_insert_id_tables) throws Exception{
String os = System.getProperty("os.name");
if (os.toLowerCase().startsWith("win")) {
generatorConfig_vm = MybatisGeneratorUtil.class.getResource(generatorConfig_vm).getPath().replaceFirst("/", "");
} else {
generatorConfig_vm = MybatisGeneratorUtil.class.getResource(generatorConfig_vm).getPath();
}
String targetProject = module + "/" + module + "-dao";
String basePath = MybatisGeneratorUtil.class.getResource("/").getPath().replace("/target/classes/", "").replace(targetProject, "").replaceFirst("/", "");
String generatorConfig_xml = MybatisGeneratorUtil.class.getResource("/").getPath().replace("/target/classes/", "") + "/src/main/resources/generatorConfig.xml";
targetProject = basePath + targetProject;
String sql = "SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE table_schema = '" + database + "' AND table_name LIKE '" + table_prefix + "_%';";
System.out.println("========== 开始生成generatorConfig.xml文件 ==========");
List<Map<String, Object>> tables = new ArrayList<>();
try {
VelocityContext context = new VelocityContext();
Map<String, Object> table;
// 查询定制前缀项目的所有表
JdbcUtil jdbcUtil = new JdbcUtil(jdbc_driver, jdbc_url, jdbc_username, AESUtil.AESDecode(jdbc_password));
List<Map> result = jdbcUtil.selectByParams(sql, null);
for (Map map : result) {
System.out.println(map.get("TABLE_NAME"));
table = new HashMap<>();
table.put("table_name", map.get("TABLE_NAME"));
table.put("model_name", lineToHump(ObjectUtils.toString(map.get("TABLE_NAME"))));
tables.add(table);
}
jdbcUtil.release();
String targetProject_sqlMap = basePath + module + "/" + module + "-rpc-service";
context.put("tables", tables);
context.put("generator_javaModelGenerator_targetPackage", package_name + ".dao.model");
context.put("generator_sqlMapGenerator_targetPackage", package_name + ".dao.mapper");
context.put("generator_javaClientGenerator_targetPackage", package_name + ".dao.mapper");
context.put("targetProject", targetProject);
context.put("targetProject_sqlMap", targetProject_sqlMap);
context.put("generator_jdbc_password", AESUtil.AESDecode(jdbc_password));
context.put("last_insert_id_tables", last_insert_id_tables);
VelocityUtil.generate(generatorConfig_vm, generatorConfig_xml, context);
// 删除旧代码
deleteDir(new File(targetProject + "/src/main/java/" + package_name.replaceAll("\\.", "/") + "/dao/model"));
deleteDir(new File(targetProject + "/src/main/java/" + package_name.replaceAll("\\.", "/") + "/dao/mapper"));
deleteDir(new File(targetProject_sqlMap + "/src/main/java/" + package_name.replaceAll("\\.", "/") + "/dao/mapper"));
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("========== 结束生成generatorConfig.xml文件 ==========");
System.out.println("========== 开始运行MybatisGenerator ==========");
List<String> warnings = new ArrayList<>();
File configFile = new File(generatorConfig_xml);
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(true);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
for (String warning : warnings) {
System.out.println(warning);
}
System.out.println("========== 结束运行MybatisGenerator ==========");
}
// 递归删除非空文件夹
public static void deleteDir(File dir) {
if (dir.isDirectory()) {
File[] files = dir.listFiles();
for (int i = 0; i < files.length; i++) {
deleteDir(files[i]);
}
}
dir.delete();
}
/**
* 下划线转驼峰
* @param str
* @return
*/
public static String lineToHump(String str) {
if (null == str || "".equals(str)) {
return str;
}
str = str.toLowerCase();
Matcher matcher = Pattern.compile("_(\\w)").matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
}
matcher.appendTail(sb);
str = sb.toString();
str = str.substring(0, 1).toUpperCase() + str.substring(1);
return str;
}
}
}
public class VelocityUtil {
/**
* 根据模板生成文件
* @param inputVmFilePath 模板路径
* @param outputFilePath 输出文件路径
* @param context
* @throws Exception
*/
public static void generate(String inputVmFilePath, String outputFilePath, VelocityContext context) throws Exception {
try {
Properties properties = new Properties();
properties.setProperty(VelocityEngine.FILE_RESOURCE_LOADER_PATH, getPath(inputVmFilePath));
Velocity.init(properties);
//VelocityEngine engine = new VelocityEngine();
Template template = Velocity.getTemplate(getFile(inputVmFilePath), "utf-8");
File outputFile = new File(outputFilePath);
FileWriterWithEncoding writer = new FileWriterWithEncoding(outputFile, "utf-8");
template.merge(context, writer);
writer.close();
} catch (Exception ex) {
throw ex;
}
}
/**
* 根据文件绝对路径获取目录
* @param filePath
* @return
*/
public static String getPath(String filePath) {
String path = "";
if (StringUtils.isNotBlank(filePath)) {
path = filePath.substring(0, filePath.lastIndexOf("/") + 1);
}
return path;
}
/**
* 根据文件绝对路径获取文件
* @param filePath
* @return
*/
public static String getFile(String filePath) {
String file = "";
if (StringUtils.isNotBlank(filePath)) {
file = filePath.substring(filePath.lastIndexOf("/") + 1);
}
return file;
}
}
模板文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!-- 配置文件 -->
<properties resource="generator.properties"></properties>
<context id="MysqlContext" targetRuntime="MyBatis3" defaultModelType="flat">
<property name="javaFileEncoding" value="UTF-8"/>
<!-- 由于beginningDelimiter和endingDelimiter的默认值为双引号("),在Mysql中不能这么写,所以还要将这两个默认值改为` -->
<property name="beginningDelimiter" value="`"/>
<property name="endingDelimiter" value="`"/>
<!-- 为生成的Java模型创建一个toString方法 -->
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"></plugin>
<!-- 为生成的Java模型类添加序列化接口,并生成serialVersionUID字段 -->
<plugin type="com.zheng.common.plugin.SerializablePlugin">
<property name="suppressJavaInterface" value="false"/>
</plugin>
<!-- 生成一个新的selectByExample方法,这个方法可以接收offset和limit参数,主要用来实现分页,只支持mysql(已使用pagehelper代替) -->
<!--<plugin type="com.zheng.common.plugin.PaginationPlugin"></plugin>-->
<!-- 生成在XML中的<cache>元素 -->
<plugin type="org.mybatis.generator.plugins.CachePlugin">
<!-- 使用ehcache -->
<property name="cache_type" value="org.mybatis.caches.ehcache.LoggingEhcache" />
<!-- 内置cache配置 -->
<!--
<property name="cache_eviction" value="LRU" />
<property name="cache_flushInterval" value="60000" />
<property name="cache_readOnly" value="true" />
<property name="cache_size" value="1024" />
-->
</plugin>
<!-- Java模型生成equals和hashcode方法 -->
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin"></plugin>
<!-- 生成的代码去掉注释 -->
<commentGenerator type="com.zheng.common.plugin.CommentGenerator">
<property name="suppressAllComments" value="true" />
<property name="suppressDate" value="true"/>
</commentGenerator>
<!-- 数据库连接 -->
<jdbcConnection driverClass="${generator.jdbc.driver}"
connectionURL="${generator.jdbc.url}"
userId="${generator.jdbc.username}"
password="${generator_jdbc_password}" />
<!-- model生成 -->
<javaModelGenerator targetPackage="${generator_javaModelGenerator_targetPackage}" targetProject="${targetProject}/src/main/java" />
<!-- MapperXML生成 -->
<sqlMapGenerator targetPackage="${generator_sqlMapGenerator_targetPackage}" targetProject="${targetProject_sqlMap}/src/main/java" />
<!-- Mapper接口生成 -->
<javaClientGenerator targetPackage="${generator_javaClientGenerator_targetPackage}" targetProject="${targetProject}/src/main/java" type="XMLMAPPER" />
<!-- 需要映射的表 -->
#foreach($table in $tables)
#if($last_insert_id_tables.containsKey($!table.table_name) == true)
<table tableName="$!table.table_name" domainObjectName="$!table.model_name">
<generatedKey column="$!last_insert_id_tables.get($!table.table_name)" sqlStatement="MySql" identity="true"/>
</table>
#else
<table tableName="$!table.table_name" domainObjectName="$!table.model_name"></table>
#end
#end
</context>
</generatorConfiguration>
可参考项目 https://github.com/shanjunmei/mybatis-generator
https://github.com/shuzheng/zheng/blob/master/zheng-common/src/main/java/com/zheng/common/util/MybatisGeneratorUtil.java