1. 程式人生 > >Mybatis逆向工程工具改進版(Version 1.1)

Mybatis逆向工程工具改進版(Version 1.1)

相對於原始版本,有以下改進:

  1. 可指定生成的實體類繼承指定SuperClass
  2. 提取Mapper方法,抽象成一個SuperMapper介面,所有的Mapper介面繼承此介面,以泛型指定key和model的型別
  3. 調整了一些程式碼的結構和邏輯

先看工具類結構:
這裡寫圖片描述

相比以前僅僅是類名修改了,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) {