Mybatis逆向工程工具改進版(Version 1.1)
阿新 • • 發佈:2019-01-14
相對於原始版本,有以下改進:
- 可指定生成的實體類繼承指定SuperClass
- 提取Mapper方法,抽象成一個SuperMapper介面,所有的Mapper介面繼承此介面,以泛型指定key和model的型別
- 調整了一些程式碼的結構和邏輯
先看工具類結構:
相比以前僅僅是類名修改了,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<K, T> {
/**
* 根據主鍵刪除資料庫的記錄
*
* @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<Integer, BankAccount> {
}
接下來按順序看看每個類的原始碼:
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<String> 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<TableConfiguration> generateTableConfigurations(Context context) {
List<TableConfiguration> configurations = new ArrayList<TableConfiguration>();
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<String, String> 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<Parameter> 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("<!--" + MergeConstants.NEW_ELEMENT_TAG + "-->")); //$NON-NLS-1$
}
@Override
public void addRootComment(XmlElement rootElement) {
int size = rootElement.getElements().size();
rootElement.addElement(size, new TextElement("<!--################################"
+ " Mybatis逆向工程生成,請勿編輯! " + "################################-->"));
}
}
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<String> generatedFilePath;
public SuperClassAppender(Set<String> 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<String> 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<String> 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<String> readFile(File file) {
List<String> 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<String> 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<String> 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<String> 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<Key,Target>
*
* @param content
*/
private List<String> insertSuperMapper(List<String> 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<String> 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<String> 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<String> 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<String> 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<String>());
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<String> 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) {