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) {
                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<String> convertTableToClassName(List<String> tables) {
        List<String> classes = new ArrayList<String>();
        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("\\"));
    }

}
关于Frmresize1.1的说明: FRMRESIZE VERSION 1.1 (CLASS) ---------------------- 作者: SUNVFP Mail: SUNVFP@EYOU.COM 开发日期:2002.10.27 改进日期:2002.10.29 简介: ------------ 有许多狐友关心不同分辨率下表单的显示问题和改变表单大小时表单内各控件的相应改变问题, 也有相当多的狐友写过这方面的类。 我所见过的resize类在使用中都有大大小小的不如人意之处: 如:vfp自己的resize类示例,不支持嵌套等;老外resize类,需要fll动态链接库支持,并且当 控件数量超过256时就会出错等。还有算法不严密,造成控件失位,Frmresize1.0就有这方面的BUG。 我决定重新写一个,代码很简单。大家不妨试用一下,并请不吝提出要求,以方便改进,使之成 为通用类,使后来者少走弯路。 1)Frmresize可以改变字体的相应大小; 2)Frmresize没有嵌套层数的限制,没有控件数量的限制; 3)Frmresize可以有选择的控制各控件是否改变,及如何改变; 4)Frmresize可以自适应不同分辨率。 改进: —————————— 1)增加NoHeightClassList 、NoHeightObjectList 两个属性,可以控制控件的高度是否变化; 2)改进算法,彻底消除失位现象。不论如何调整表单大小,控件的相对位置都不会失位!; 3)因算法的变化,去掉两个不再需要的自定义属性。 用法: ----- 要使用 Frmresize 1.1 必须按以下两步: 添加该类到你的表单, 并设置它的属性. 1) 将 Frmresize 类拖放到你的表单 2) 设置它的属性: 设置类的 FontResize 属性 (在属性窗口的末端) (.T.(默认值)-字体随着作相应大小的改变,.F.-字体大小不变) 设置类的 NoPosClassList 属性 (在属性窗口的末端) (不作位置变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoSizeClassList 属性 (不作大小变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoPosObjectList 属性 (不作位置变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 设置类的 NoSizeObjectList 属性 (不作大小变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 设置类的 NoHeightClassList 属性 (不作高度变化的基类列表,例如commandbutton text等,timer custom hyperlink已排除) 设置类的 NoHeightObjectList 属性 (不作高度变化的控件名称列表,例如txt1 COMMAND1 CMDOK等) 注:1。当NoPosClassList和NoSizeClassList 或 NoPosObjectList和NoSizeObjectList中同时包含时,则相关控件及其下层控件不变化。 2。NoSizeClassList或NoSizeObjectList中的相应控件的字体大小不变。 3) 在表单resize事件中写:this.frmresize1.frmresize() 注: 不带任何参数。 4) 在表单init事件中写:this.frmresize1.frmresize(800,600) 注: 参数800,600是指你开发时的屏幕分辨率。 许可: 该类可以没有问题地添加到你的项目. 我要求的唯一东西是如果你改进了程序请让我知道. 发送 Bug 和建议到: sunvfp@eyou.com 或到天堂论坛进行讨论 www.myf1.net
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值