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

相对于原始版本,有以下改进:

  1. 可指定生成的实体类继承指定SuperClass
  2. 提取Mapper方法,抽象成一个SuperMapper接口,所有的Mapper接口继承此接口,以泛型指定key和model的类型
  3. 调整了一些代码的结构和逻辑

先看工具类结构:
Mybatis逆向工程工具改进版(Version 1.1)_第1张图片

相比以前仅仅是类名修改了,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("\\"));
    }

}

你可能感兴趣的:(mybatis,造轮子)