相对于原始版本,有以下改进:
相比以前仅仅是类名修改了,SuperClassAppender这个类负责追加SuperModel,SuperMapper。
SuperClass:
/**
* 基础Model
*
* @author wb-jjb318191
* @create 2018-01-16 13:16
*/
public class BaseModel implements Serializable {
private static final long serialVersionUID = -7192344879797520674L;
/**
* 当前页,初始值为1
*/
private Integer currentPage = 1;
/**
* 起始记录数,初始值为1
*/
private Integer startRow = 1;
/**
* 页大小,初始值为10
*/
private Integer limit = 10;
public Integer getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Integer currentPage) {
this.currentPage = currentPage;
}
public Integer getStartRow() {
if (currentPage != null && currentPage > 0) {
startRow = (currentPage - 1) * limit;
}
return startRow;
}
public void setStartRow(Integer startRow) {
this.startRow = startRow;
}
public Integer getLimit() {
return limit;
}
public void setLimit(Integer limit) {
this.limit = limit;
}
}
/**
* Mapper基类接口
*
* @author wb-jjb318191
* @create 2017-12-06 9:23
*/
public interface BaseMapper {
/**
* 根据主键删除数据库的记录
*
* @param id
* @return
*/
int deleteByPrimaryKey(K id);
/**
* 新写入数据库记录
*
* @param record
* @return
*/
int insert(T record);
/**
* 动态字段,写入数据库记录
*
* @param record
* @return
*/
int insertSelective(T record);
/**
* 根据指定主键获取一条数据库记录
*
* @param id
* @return
*/
T selectByPrimaryKey(K id);
/**
* 动态字段,根据主键来更新符合条件的数据库记录
*
* @param record
* @return
*/
int updateByPrimaryKeySelective(T record);
/**
* 根据主键来更新符合条件的数据库记录
*
* @param record
* @return
*/
int updateByPrimaryKey(T record);
}
指定生成的Model继承自BaseModel,Mapper接口继承自BaseMapper,使用泛型指定Key和Model的类型,如以下形式:
/**
* 数据库表:bank_account
*
* @author wb-jjb318191
* @create 2018-03-30
*/
public class BankAccount extends BaseModel {
......
}
import com.bob.common.entity.base.BaseMapper;
import com.bob.web.mvc.entity.model.BankAccount;
/**
* @author wb-jjb318191
* @create 2018-03-30
*/
public interface BankAccountMapper extends BaseMapper {
}
接下来按顺序看看每个类的源码:
GeneratorContextConfig:逆向工程的配置接口,指定各种配置信息。
/**
* Mybatis逆向工程配置
* 表名太长可能导致MapperInterface和Mapper.xml内方法缺失
* 若出现这种情况,建议先缩短表名,逆向工程完成后再手动还原,修改生成的相关类名
*
* @author wb-jjb318191
* @create 2017-09-30 9:19
*/
interface GeneratorContextConfig {
//是否用逆向工程生成的Model,Dao,Mapper覆盖当前已存在的,若覆盖请做好备份工作
Boolean OVERRIDE_EXIST = false;
//指定要生成的Table
List TABLES = Arrays.asList("bank_account");
//连接数据库驱动包 这里选择自己本地位置,也可以将驱动放在项目的resources文件夹内
String CLASSPATH_ENTRY = "common/src/main/resources/mysql-connector-java-5.1.44-bin.jar";
//String CLASSPATH_ENTRY = "D:/profile/postgresql-42.1.4.jar";
//指定生成java文件的编码格式
String JAVA_FILEEN_CODING = "UTF-8";
//指定JDBC信息
String JDBC_DRIVERCLASS = "com.mysql.jdbc.Driver";
String JDBC_CONNECTIONURL = "jdbc:mysql://localhost:3306/project";
String JDBC_USER_NAME = "root";
String JDBC_PASSWORD = "lanboal";
//如果maven工程只是单独的一个工程,targetProject="src/main/resources"
//String DEFAULT_JAVA_TARGET_PROJECT = "src/main/java";
//String DEFAULT_RESOURCES_TARGET_PROJECT = "src/main/resources";
//若果maven工程是分模块的工程,即使时在当前模块下生产成Mybatis文件,也需要指定模块前缀,
// targetProject="指定模块的名称/路径",例如:targetProject="project-web/src/main/java"
String DEFAULT_JAVA_TARGET_PROJECT = "web/src/main/java";
//java类和配置文件生成位置可以指向不同的项目
String DEFAULT_RESOURCES_TARGET_PROJECT = "web/src/main/resources";
//指定Java Model生成位置
String JAVA_MODEL_TARGET_PROJECT = DEFAULT_JAVA_TARGET_PROJECT;
String JAVA_MODEL_TARGET_PACKAGE = "com.bob.web.mvc.entity.model";
//指定Java DAO接口生成位置
String JAVACLIENT_TARGET_PROJECT = DEFAULT_JAVA_TARGET_PROJECT;
String JAVACLIENT_TARGET_PACKAGE = "com.bob.web.mvc.mapper";
//指定Mapper.xml生成位置
String SQLMAP_TARGET_PROJECT = DEFAULT_RESOURCES_TARGET_PROJECT;
String SQLMAP_TARGET_PACKAGE = "com.bob.web.mvc.mapper";
//是否为生成的Model添加父类
boolean APPEND_SUPER_MODEL = true;
String SUPER_MODEL_NAME = BaseModel.class.getName();
//是否为生成的Mapper添加父类
boolean APPEND_SUPER_MAPPER = true;
String SUPER_MAPPER_NAME = BaseMapper.class.getName();
/**
* 可设置自定义的类型解析器
* {@linkplain JavaTypeResolver}
*/
String JAVA_TYPE_RESOLVER = null;
GeneratorConfigurationManager:将配置信息注入到逆向工程的执行类中
import java.util.ArrayList;
import java.util.List;
import org.mybatis.generator.config.CommentGeneratorConfiguration;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.Context;
import org.mybatis.generator.config.JDBCConnectionConfiguration;
import org.mybatis.generator.config.JavaClientGeneratorConfiguration;
import org.mybatis.generator.config.JavaModelGeneratorConfiguration;
import org.mybatis.generator.config.JavaTypeResolverConfiguration;
import org.mybatis.generator.config.SqlMapGeneratorConfiguration;
import org.mybatis.generator.config.TableConfiguration;
/**
* Mybatis逆向工程基于Java形式的配置类
*
* @author wb-jjb318191
* @create 2017-09-30 9:17
*/
class GeneratorConfigurationManager {
public Configuration configMybatisGenerator() {
Configuration configuration = new Configuration();
configuration.addClasspathEntry(System.getProperty("user.dir") + "\\" + GeneratorContextConfig.CLASSPATH_ENTRY);
Context context = new Context(null);
context.setTargetRuntime("MyBatis3");
context.setId("wb-jjb318191");
context.addProperty("javaFileEncoding", GeneratorContextConfig.JAVA_FILEEN_CODING);
//设置注解生成器
context.setCommentGeneratorConfiguration(generateCommentConfiguration());
//设置JDBC连接配置
context.setJdbcConnectionConfiguration(generateJDBCConnectionConfiguration());
//设置JDBC Type 与Java Type之间的映射解析器
context.setJavaTypeResolverConfiguration(generateJavaTypeResolverConfiguration());
//设置Java Model生成配置
context.setJavaModelGeneratorConfiguration(generateJavaModelGeneratorConfiguration());
//设置DAO层的生成配置
context.setSqlMapGeneratorConfiguration(generateSqlMapGeneratorConfiguration());
//设置Mapper.xml生成
context.setJavaClientGeneratorConfiguration(generateJavaClientGeneratorConfiguration());
//设置需要生成的Table及生成形式
for (TableConfiguration tableConfiguration : generateTableConfigurations(context)) {
context.addTableConfiguration(tableConfiguration);
}
configuration.addContext(context);
return configuration;
}
/**
* 配置注解生成器
*
* @return
*/
private CommentGeneratorConfiguration generateCommentConfiguration() {
CommentGeneratorConfiguration configuration = new CommentGeneratorConfiguration();
configuration.setConfigurationType(GeneralCommentGenerator.class.getName());
//是否去除自动生成的注释 true:是 : false:否
configuration.addProperty("suppressAllComments", "false");
configuration.addProperty("addRemarkComments", "true");
return configuration;
}
/**
* 设置数据库连接的信息:驱动类、连接地址、用户名、密码
*
* @return
*/
private JDBCConnectionConfiguration generateJDBCConnectionConfiguration() {
JDBCConnectionConfiguration configuration = new JDBCConnectionConfiguration();
configuration.setDriverClass(GeneratorContextConfig.JDBC_DRIVERCLASS);
String jdbcSuffix = "?useUnicode=true&characterEncoding=UTF8&useSSL=false";
configuration.setConnectionURL(GeneratorContextConfig.JDBC_CONNECTIONURL + jdbcSuffix);
configuration.setUserId(GeneratorContextConfig.JDBC_USER_NAME);
configuration.setPassword(GeneratorContextConfig.JDBC_PASSWORD);
return configuration;
}
/**
* 设置JDBC Type 与Java Type之间的映射解析器
*
* @return
*/
private JavaTypeResolverConfiguration generateJavaTypeResolverConfiguration() {
JavaTypeResolverConfiguration configuration = new JavaTypeResolverConfiguration();
//可自定义类型映射解析器
configuration.setConfigurationType(GeneratorContextConfig.JAVA_TYPE_RESOLVER);
//默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal
configuration.addProperty("forceBigDecimals", "true");
return configuration;
}
/**
* 配置Java Model生成
*
* @return
*/
private JavaModelGeneratorConfiguration generateJavaModelGeneratorConfiguration() {
JavaModelGeneratorConfiguration configuration = new JavaModelGeneratorConfiguration();
configuration.setTargetProject(GeneratorContextConfig.JAVA_MODEL_TARGET_PROJECT);
configuration.setTargetPackage(GeneratorContextConfig.JAVA_MODEL_TARGET_PACKAGE);
//是否让schema作为包的后缀
configuration.addProperty("enableSubPackages", "false");
//从数据库返回的值被清理前后的空格
configuration.addProperty("trimStrings", "true");
return configuration;
}
/**
* 配置Mapper.xml生成
*
* @return
*/
private SqlMapGeneratorConfiguration generateSqlMapGeneratorConfiguration() {
SqlMapGeneratorConfiguration configuration = new SqlMapGeneratorConfiguration();
configuration.setTargetProject(GeneratorContextConfig.SQLMAP_TARGET_PROJECT);
configuration.setTargetPackage(GeneratorContextConfig.SQLMAP_TARGET_PACKAGE);
//是否让schema作为包的后缀
configuration.addProperty("enableSubPackages", "false");
return configuration;
}
/**
* 设置DAO生成
*
* @return
*/
private JavaClientGeneratorConfiguration generateJavaClientGeneratorConfiguration() {
JavaClientGeneratorConfiguration configuration = new JavaClientGeneratorConfiguration();
configuration.setConfigurationType("XMLMAPPER");
configuration.setTargetProject(GeneratorContextConfig.JAVACLIENT_TARGET_PROJECT);
configuration.setTargetPackage(GeneratorContextConfig.JAVACLIENT_TARGET_PACKAGE);
//是否让schema作为包的后缀
configuration.addProperty("enableSubPackages", "false");
return configuration;
}
private List generateTableConfigurations(Context context) {
List configurations = new ArrayList();
for (String table : GeneratorContextConfig.TABLES) {
TableConfiguration configuration = new TableConfiguration(context);
configuration.setTableName(table);
configuration.setSelectByExampleStatementEnabled(false);
configuration.setDeleteByExampleStatementEnabled(false);
configuration.setCountByExampleStatementEnabled(false);
configuration.setUpdateByExampleStatementEnabled(false);
configurations.add(configuration);
}
return configurations;
}
}
GeneralCommentGenerator:逆向工程注释生成类
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedColumn;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.dom.java.CompilationUnit;
import org.mybatis.generator.api.dom.java.Field;
import org.mybatis.generator.api.dom.java.InnerClass;
import org.mybatis.generator.api.dom.java.InnerEnum;
import org.mybatis.generator.api.dom.java.Method;
import org.mybatis.generator.api.dom.java.Parameter;
import org.mybatis.generator.api.dom.java.TopLevelClass;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;
import org.mybatis.generator.config.MergeConstants;
import org.mybatis.generator.config.PropertyRegistry;
import static org.mybatis.generator.internal.util.StringUtility.isTrue;
/**
* Mybatis逆向工程自定义注释生成器
*
* @author wb-jjb318191
* @create 2017-09-30 9:22
*/
public class GeneralCommentGenerator implements CommentGenerator {
/**
* The properties.
*/
private Properties properties;
/**
* The suppress all comments.
*/
private boolean suppressAllComments;
/**
* The addition of table remark's comments.
* If suppressAllComments is true, this option is ignored
*/
private boolean addRemarkComments;
private String currentDateStr;
private Map userEnv;
/**
* Instantiates a new default comment generator.
*/
public GeneralCommentGenerator() {
super();
properties = new Properties();
suppressAllComments = false;
addRemarkComments = false;
userEnv = System.getenv();
currentDateStr = (new SimpleDateFormat("yyyy-MM-dd")).format(new Date());
}
@Override
public void addConfigurationProperties(Properties properties) {
this.properties.putAll(properties);
suppressAllComments = isTrue(properties
.getProperty(PropertyRegistry.COMMENT_GENERATOR_SUPPRESS_ALL_COMMENTS));
addRemarkComments = isTrue(properties
.getProperty(PropertyRegistry.COMMENT_GENERATOR_ADD_REMARK_COMMENTS));
}
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
if (suppressAllComments) {
return;
}
field.addJavaDocLine("/**");
field.addJavaDocLine(" * " + introspectedColumn.getRemarks());
field.addJavaDocLine(" */");
}
@Override
public void addFieldComment(Field field, IntrospectedTable introspectedTable) {
if (suppressAllComments) {
return;
}
field.addJavaDocLine("/**");
field.addJavaDocLine(" * " + introspectedTable.getFullyQualifiedTable());
field.addJavaDocLine(" */");
}
@Override
public void addModelClassComment(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
if (suppressAllComments || !addRemarkComments) {
return;
}
topLevelClass.addJavaDocLine("/**");
topLevelClass.addJavaDocLine(" * 数据库表:" + introspectedTable.getFullyQualifiedTable());
topLevelClass.addJavaDocLine(" * ");
topLevelClass.addJavaDocLine(" * @author " + userEnv.get("USERNAME"));
topLevelClass.addJavaDocLine(" * @create " + currentDateStr);
topLevelClass.addJavaDocLine(" */");
}
@Override
public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable) {
if (suppressAllComments) {
return;
}
innerClass.addJavaDocLine("/**"); //$NON-NLS-1$
innerClass.addJavaDocLine(" * 数据库表:" + introspectedTable.getFullyQualifiedTable());
innerClass.addJavaDocLine(" * ");
innerClass.addJavaDocLine(" * @author " + userEnv.get("USERNAME"));
innerClass.addJavaDocLine(" * @create " + currentDateStr);
innerClass.addJavaDocLine(" */"); //$NON-NLS-1$
}
@Override
public void addClassComment(InnerClass innerClass, IntrospectedTable introspectedTable, boolean markAsDoNotDelete) {
addClassComment(innerClass, introspectedTable);
}
@Override
public void addEnumComment(InnerEnum innerEnum, IntrospectedTable introspectedTable) {
}
@Override
public void addGetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
if (1 == 1) { //注销getter()方法的注释
return;
}
StringBuilder sb = new StringBuilder();
method.addJavaDocLine("/**"); //$NON-NLS-1$
// method.addJavaDocLine(" * This method was generated by MyBatis Generator."); //$NON-NLS-1$
sb.append(" * 获取 "); //$NON-NLS-1$
sb.append(introspectedColumn.getRemarks()).append(" 字段:");
sb.append(introspectedTable.getFullyQualifiedTable());
sb.append('.');
sb.append(introspectedColumn.getActualColumnName());
method.addJavaDocLine(sb.toString());
method.addJavaDocLine(" *"); //$NON-NLS-1$
sb.setLength(0);
sb.append(" * @return "); //$NON-NLS-1$
sb.append(introspectedTable.getFullyQualifiedTable());
sb.append('.');
sb.append(introspectedColumn.getActualColumnName());
sb.append(", ");
sb.append(introspectedColumn.getRemarks());
method.addJavaDocLine(sb.toString());
method.addJavaDocLine(" */"); //$NON-NLS-1$
}
@Override
public void addSetterComment(Method method, IntrospectedTable introspectedTable, IntrospectedColumn introspectedColumn) {
if (1 == 1) { //注销setter()方法的注释
return;
}
StringBuilder sb = new StringBuilder();
method.addJavaDocLine("/**"); //$NON-NLS-1$
// method.addJavaDocLine(" * This method was generated by MyBatis Generator."); //$NON-NLS-1$
sb.append(" * 设置 "); //$NON-NLS-1$
sb.append(introspectedColumn.getRemarks()).append(" 字段:");
sb.append(introspectedTable.getFullyQualifiedTable());
sb.append('.');
sb.append(introspectedColumn.getActualColumnName());
method.addJavaDocLine(sb.toString());
method.addJavaDocLine(" *"); //$NON-NLS-1$
Parameter parm = method.getParameters().get(0);
sb.setLength(0);
sb.append(" * @param "); //$NON-NLS-1$
sb.append(parm.getName());
sb.append(" the value for "); //$NON-NLS-1$
sb.append(introspectedTable.getFullyQualifiedTable());
sb.append('.');
sb.append(introspectedColumn.getActualColumnName());
sb.append(", ");
sb.append(introspectedColumn.getRemarks());
method.addJavaDocLine(sb.toString());
// addJavadocTag(method, false);
method.addJavaDocLine(" */"); //$NON-NLS-1$
}
@Override
public void addGeneralMethodComment(Method method, IntrospectedTable introspectedTable) {
StringBuilder sb = new StringBuilder();
method.addJavaDocLine("/**"); //$NON-NLS-1$
sb.append(" * ");
if (method.isConstructor()) {
sb.append(" 构造查询条件");
}
String method_name = method.getName();
if ("setOrderByClause".equals(method_name)) {
sb.append(" 设置排序字段");
} else if ("setDistinct".equals(method_name)) {
sb.append(" 设置过滤重复数据");
} else if ("getOredCriteria".equals(method_name)) {
sb.append(" 获取当前的查询条件实例");
} else if ("isDistinct".equals(method_name)) {
sb.append(" 是否过滤重复数据");
} else if ("getOrderByClause".equals(method_name)) {
sb.append(" 获取排序字段");
} else if ("createCriteria".equals(method_name)) {
sb.append(" 创建一个查询条件");
} else if ("createCriteriaInternal".equals(method_name)) {
sb.append(" 内部构建查询条件对象");
} else if ("clear".equals(method_name)) {
sb.append(" 清除查询条件");
} else if ("countByExample".equals(method_name)) {
sb.append(" 根据指定的条件获取数据库记录数");
} else if ("deleteByExample".equals(method_name)) {
sb.append(" 根据指定的条件删除数据库符合条件的记录");
} else if ("deleteByPrimaryKey".equals(method_name)) {
sb.append(" 根据主键删除数据库的记录");
} else if ("insert".equals(method_name)) {
sb.append(" 新写入数据库记录");
} else if ("insertSelective".equals(method_name)) {
sb.append(" 动态字段,写入数据库记录");
} else if ("selectByExample".equals(method_name)) {
sb.append(" 根据指定的条件查询符合条件的数据库记录");
} else if ("selectByPrimaryKey".equals(method_name)) {
sb.append(" 根据指定主键获取一条数据库记录");
} else if ("updateByExampleSelective".equals(method_name)) {
sb.append(" 动态根据指定的条件来更新符合条件的数据库记录");
} else if ("updateByExample".equals(method_name)) {
sb.append(" 根据指定的条件来更新符合条件的数据库记录");
} else if ("updateByPrimaryKeySelective".equals(method_name)) {
sb.append(" 动态字段,根据主键来更新符合条件的数据库记录");
} else if ("updateByPrimaryKey".equals(method_name)) {
sb.append(" 根据主键来更新符合条件的数据库记录");
}
sb.append(",");
sb.append(introspectedTable.getFullyQualifiedTable());
method.addJavaDocLine(sb.toString());
final List parameterList = method.getParameters();
if (!parameterList.isEmpty()) {
method.addJavaDocLine(" *");
if ("or".equals(method_name)) {
sb.append(" 增加或者的查询条件,用于构建或者查询");
}
} else {
if ("or".equals(method_name)) {
sb.append(" 创建一个新的或者查询条件");
}
}
String paramterName;
for (Parameter parameter : parameterList) {
sb.setLength(0);
sb.append(" * @param "); //$NON-NLS-1$
paramterName = parameter.getName();
sb.append(paramterName);
if ("orderByClause".equals(paramterName)) {
sb.append(" 排序字段"); //$NON-NLS-1$
} else if ("distinct".equals(paramterName)) {
sb.append(" 是否过滤重复数据");
} else if ("criteria".equals(paramterName)) {
sb.append(" 过滤条件实例");
}
method.addJavaDocLine(sb.toString());
}
if (method.getReturnType() != null) {
method.addJavaDocLine(" * @return");
}
method.addJavaDocLine(" */"); //$NON-NLS-1$
}
@Override
public void addJavaFileComment(CompilationUnit compilationUnit) {
}
@Override
public void addComment(XmlElement xmlElement) {
//当XmlElement添加了@mbg.generated后,下次再执行Mybatis Generate时不会重复生成此元素,但会将此元素还原成最初版本
xmlElement.addElement(new TextElement("")); //$NON-NLS-1$
}
@Override
public void addRootComment(XmlElement rootElement) {
int size = rootElement.getElements().size();
rootElement.addElement(size, new TextElement(""));
}
}
ProgressCallbackAdapter:处理进度接口适配器类。
import org.mybatis.generator.api.ProgressCallback;
/**
* 处理进度接口适配器类
*
* @author Administrator
* @create 2018-03-29 22:46
*/
class ProgressCallbackAdapter implements ProgressCallback {
@Override
public void introspectionStarted(int totalTasks) {
}
@Override
public void generationStarted(int totalTasks) {
}
@Override
public void saveStarted(int totalTasks) {
}
@Override
public void startTask(String taskName) {
}
@Override
public void done() {
}
@Override
public void checkCancel() throws InterruptedException {
}
}
SuperClassAppender:SuperClass的追加类。
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import static com.bob.common.utils.mybatis.generate.GeneratorContextConfig.APPEND_SUPER_MAPPER;
import static com.bob.common.utils.mybatis.generate.GeneratorContextConfig.APPEND_SUPER_MODEL;
import static com.bob.common.utils.mybatis.generate.GeneratorContextConfig.SUPER_MAPPER_NAME;
import static com.bob.common.utils.mybatis.generate.GeneratorContextConfig.SUPER_MODEL_NAME;
/**
* 基础Mapper,基础Model 设置类
*
* @author Administrator
* @create 2018-03-29 22:47
*/
class SuperClassAppender extends ProgressCallbackAdapter {
private Set generatedFilePath;
public SuperClassAppender(Set generatedFilePath) {
this.generatedFilePath = generatedFilePath;
if (APPEND_SUPER_MODEL && !isClassExists(SUPER_MODEL_NAME)) {
throw new IllegalStateException(String.format("[%s]不存在", SUPER_MODEL_NAME));
}
if (APPEND_SUPER_MAPPER && !isClassExists(SUPER_MAPPER_NAME)) {
throw new IllegalStateException(String.format("[%s]不存在", SUPER_MAPPER_NAME));
}
}
@Override
public void done() {
for (String path : generatedFilePath) {
if (path.substring(0, path.lastIndexOf(".")).endsWith("Mapper") && APPEND_SUPER_MAPPER) {
appendSuperMapper(path);
} else if (APPEND_SUPER_MODEL) {
appendSuperModel(path);
}
}
}
/**
* 向指定的Java文件追加父类
*
* @param javaPath
*/
private void appendSuperModel(String javaPath) {
File model = getFile(javaPath);
List content = readFile(model);
insertImportLine(content, SUPER_MODEL_NAME);
insertSuperModel(content);
writeFile(model, content);
}
/**
* 向指定的Mapper接口追加父接口
*
* @param mapperPath
*/
private void appendSuperMapper(String mapperPath) {
File mapper = getFile(mapperPath);
List content = readFile(mapper);
insertImportLine(content, SUPER_MAPPER_NAME);
writeFile(mapper, insertSuperMapper(content));
}
/**
* @param path
* @return
*/
private File getFile(String path) {
Assert.hasText(path, "文件路径不能为空");
File file = new File(path);
Assert.isTrue(file.exists(), String.format("[%s]不存在", path));
return file;
}
/**
* 读取文件内容
*
* @param file
* @retur
*/
private List readFile(File file) {
List content;
try {
content = FileUtils.readLines(file, "UTF-8");
} catch (IOException e) {
throw new IllegalArgumentException(String.format("[%s]文件不可读", file.getAbsolutePath()), e);
}
return content;
}
/**
* 添加Import行,import行的顺序不保证,自行格式化
*
* @param content
* @param className
*/
private void insertImportLine(List content, String className) {
String importLine = "import " + className + ";";
for (int i = 0; i < content.size(); i++) {
String line = content.get(i);
if (line.startsWith("import")) {
content.add(i, importLine);
return;
}
//当碰到public时,说明到了Class定义行,终止循环
if (line.startsWith("public")) {
break;
}
}
content.add(2, importLine);
}
/**
* 将修改后的内容覆盖原先的
*
* @param file
* @param content
*/
private void writeFile(File file, List content) {
try {
FileUtils.writeLines(file, content, false);
} catch (IOException e) {
throw new IllegalStateException(String.format("写入[%s]文件出现异常"), e);
}
}
/**
* 插入 extends BaseModel
*
* @param content
*/
private void insertSuperModel(List content) {
int classLineIndex = inspectClassLineIndex(content);
String insertWord = "extends " + SUPER_MODEL_NAME.substring(SUPER_MODEL_NAME.lastIndexOf(".") + 1);
String newClassLine = content.get(classLineIndex).replace("{", insertWord + " {");
content.set(classLineIndex, newClassLine);
}
/**
* 插入 extends BaseMapper
*
* @param content
*/
private List insertSuperMapper(List content) {
int classLineIndex = inspectClassLineIndex(content);
String key = getTypeString(content, "deleteByPrimaryKey");
String target = getTypeString(content, "insertSelective");
String insertWords = "extends " + SUPER_MAPPER_NAME.substring(SUPER_MAPPER_NAME.lastIndexOf(".") + 1) + "<" + key + "," + target + ">";
String newClassLine = content.get(classLineIndex).replace("{", insertWords + " {");
content = content.subList(0, classLineIndex);
appendMapperComments(content);
content.add(newClassLine);
content.add("}");
return content;
}
/**
* 为Mapper接口添加注释
*
* @param content
*/
private void appendMapperComments(List content) {
StringBuffer sb = new StringBuffer();
String newLineWord = System.getProperty("line.separator");
String dateLine = (new SimpleDateFormat("yyyy-MM-dd")).format(new Date());
sb.append("/**").append(newLineWord)
.append(" * @author " + System.getenv("USERNAME")).append(newLineWord)
.append(" * @create " + dateLine).append(newLineWord)
.append(" */");
content.add(sb.toString());
}
/**
* 获取Mapper的Key,Target类型的字符串
*
* @param content
* @param keyword
* @return
*/
private String getTypeString(List content, String keyword) {
for (String line : content) {
if (line.contains(keyword)) {
String argBody = line.substring(line.indexOf("(") + 1, line.indexOf(")"));
return argBody.split(" ")[0];
}
}
return null;
}
/**
* 获取类定义行
*
* @param content
* @return
*/
private int inspectClassLineIndex(List content) {
int classLineIndex = 0;
for (int i = 0; i < content.size(); i++) {
if (content.get(i).startsWith("public")) {
classLineIndex = i;
break;
}
}
return classLineIndex;
}
/**
* @param className
*/
private boolean isClassExists(String className) {
return ClassUtils.isPresent(className, ClassUtils.getDefaultClassLoader());
}
}
MybatisGenerator:逆向工程执行者
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Mybatis逆向工程执行者
* 基于Mybatis Generator 1.3.5 Release
*
* @author wb-jjb318191
* @create 2017-09-28 17:08
*/
public class MybatisGenerator {
private static final Logger LOGGER = LoggerFactory.getLogger(MybatisGenerator.class);
/**
* 生成的java文件地址集合
*/
private static Set generatedJavaPaths = new HashSet<>();
private static AtomicBoolean executed = new AtomicBoolean(false);
/**
* Main函数,执行逆向工程
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
MybatisGenerator.generate();
}
/**
* 执行逆向工程
* 使用配置好的执行策略{@linkplain GeneratorContextConfig}
*
* @throws Exception
* @see GeneratorContextConfig
*/
private static void generate() throws Exception {
new MybatisGenerator().generate(GeneratorContextConfig.OVERRIDE_EXIST);
//执行第二次的原因是为了让Mapper.xml里有两行注释,包围由逆向工程生成的元素
new MybatisGenerator().generate(true);
}
/**
* 执行逆向工程
*
* @param override 是否覆盖已存在的Model,Dao,Mapper
* @throws Exception
*/
private void generate(boolean override) throws Exception {
if (!override & inspectGeneratedFilesExists()) {
String over = GeneratorContextConfig.class.getSimpleName() + "." + "OVERRIDE_EXIST";
throw new IllegalStateException(String.format("逆向工程生成的文件将会覆盖已存在文件,请确认做好备份后设置[%s]属性为true,执行后请还原为false", over));
}
Configuration config = new GeneratorConfigurationManager().configMybatisGenerator();
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, new DefaultShellCallback(true), new ArrayList());
myBatisGenerator.generate(new SuperClassAppender(generatedJavaPaths));
}
/**
* 检测将通过逆向工程生成的Model,Dao,Mapper是否已存在
*
* @throws Exception
*/
private boolean inspectGeneratedFilesExists() throws Exception {
//每次运行执行两次mybatis逆向工程,第二次时文件肯定已存在,不检查
if (!executed.compareAndSet(false, true)) {
return true;
}
LOGGER.info("非覆盖式执行Mybatis Generate,检查将要生成的文件是否已存在!");
List classNames = convertTableToClassName(GeneratorContextConfig.TABLES);
String mapperPackage = replaceDotByDelimiter(GeneratorContextConfig.SQLMAP_TARGET_PACKAGE);
String warnMsg = "即将覆盖{} [{}] ";
boolean exists = false;
for (String clazzName : classNames) {
String modelName = GeneratorContextConfig.JAVA_MODEL_TARGET_PACKAGE + "." + clazzName;
if (exists = isClassExists(modelName) || exists) {
LOGGER.warn(warnMsg, "Model Class", modelName);
}
String daoName = GeneratorContextConfig.JAVACLIENT_TARGET_PACKAGE + "." + clazzName + "Mapper";
if (exists = isClassExists(daoName) || exists) {
LOGGER.warn(warnMsg, "DAO Class", daoName);
}
String mapperPath = mapperPackage + "/" + clazzName + "Mapper.xml";
if (exists = isMapperExists(mapperPath) || exists) {
LOGGER.warn(warnMsg, "Mapper XML", mapperPath);
}
}
return exists;
}
/**
* 依据驼峰原则格式化将表名转换为类名,当遇到下划线时去除下划线并对之后的一位字符大写
*
* @param tables
* @return
*/
private List convertTableToClassName(List tables) {
List classes = new ArrayList();
for (String table : tables) {
classes.add(convertTableToClassName(table));
}
return classes;
}
/**
* 依据驼峰原则格式化将表名转换为类名,当遇到下划线时去除下划线并对之后的一位字符大写
*
* @param table
* @return
*/
private String convertTableToClassName(String table) {
Assert.hasText(table, "表名不能为空");
StringBuilder sb = new StringBuilder();
sb.append(toUpperCase(table.charAt(0)));
for (int i = 1; i < table.length(); i++) {
sb.append('_' == table.charAt(i) ? toUpperCase(table.charAt(++i)) : table.charAt(i));
}
return sb.toString();
}
/**
* 将字符转换为大写
*
* @param ch
* @return
*/
private char toUpperCase(char ch) {
return Character.toUpperCase(ch);
}
/**
* 使用'/'替换路径中的'.'
*
* @param path
* @return
*/
private String replaceDotByDelimiter(String path) {
Assert.hasText(path, "替换路径不能为空");
return StringUtils.replace(path, ".", "/");
}
/**
* 项目是否是多模块项目
*
* @return
*/
private boolean isMultiModuleProject() {
return !GeneratorContextConfig.DEFAULT_JAVA_TARGET_PROJECT.startsWith("src");
}
/**
* 验证类是否存在
*
* @param className
* @return
*/
private boolean isClassExists(String className) throws IOException {
Assert.hasText(className, "类名不能为空");
String absPath = this.getRootPath() + "/" + GeneratorContextConfig.DEFAULT_JAVA_TARGET_PROJECT + "/" + replaceDotByDelimiter(className)
+ ".java";
generatedJavaPaths.add(absPath);
return new FileSystemResource(absPath).exists();
}
/**
* 验证文件是否存在
*
* @param mapperPath
* @return
*/
public boolean isMapperExists(String mapperPath) throws IOException {
Assert.hasText(mapperPath, "Mapper路径不能为空");
String absPath = this.getRootPath() + "/" + GeneratorContextConfig.DEFAULT_RESOURCES_TARGET_PROJECT + "/" + mapperPath;
return new FileSystemResource(absPath).exists();
}
/**
* 获取项目根路径
*
* @return
* @throws IOException
*/
private String getRootPath() throws IOException {
String classPath = this.replaceDotByDelimiter(this.getClass().getName()) + ".class";
Resource resource = new ClassPathResource(classPath);
String path = resource.getFile().getAbsolutePath();
path = path.substring(0, path.indexOf("\\target"));
return path.substring(0, path.lastIndexOf("\\"));
}
}