MyBatis Generator的一个问题引发对插件的修改

在使用mybatis.generator插件自动生成mapper.xml的时候发现一个问题:默认生成的dao接口为mapper结尾

mapper结尾

当然我们知道在不同的ORM框架中名称表示不同,例如:mybatis中称为Mapper,spring Data JPA中称为Repository,但是习惯用***Dao结尾表示数据访问层接口的应该怎么办?

其实mybatis generator支持修改这个后缀:通过generatorConfig.xml配置文件添加table标签的mapperName属性,但是修改后会存在另一个问题:生成的xml由原本的Mapper结尾变成了Dao结尾,也就是只能跟设置的mapperName属性一致,网上搜索了相关问题,只发现一个通过修改插件源码中的calculateMyBatis3XmlMapperFileName方法的解决方案。
接下来说下我的处理过程,主要涉及下面几点:

  • generator插件的使用
  • maven创建自定义插件
  • 插件的调试(远程调试)
  • generator源码的修改
先说下MyBatis Generator插件的使用

1.pom.xml添加依赖

<!-- 使用MyBatis Generator插件自动生成代码 -->
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.4</version>
    <configuration>
        <!--配置文件的路径-->
        <configurationFile>${basedir}/src/main/resources/generator/mybatis/generatorConfig.xml</configurationFile>
        <verbose>true</verbose>
        <overwrite>true</overwrite>
    </configuration>
</plugin>

2.generatorConfig.xml的配置示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!--导入属性配置-->
    <properties resource="generator/mybatis/db.properties"/>

    <!--指定特定数据库的jdbc驱动jar包的位置-->
    <classPathEntry location="${jdbc.driverLocation}"/>

    <context id="default" targetRuntime="MyBatis3">

        <!-- optional,旨在创建class时,对注释进行控制 -->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--jdbc的数据库连接 -->
        <jdbcConnection
                driverClass="${jdbc.driverClass}"
                connectionURL="${jdbc.connectionURL}"
                userId="${jdbc.userId}"
                password="${jdbc.password}">
        </jdbcConnection>


        <!-- 非必需,类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>


        <!-- Model模型生成器,用来生成含有主键key的类,记录类 以及查询Example类
            targetPackage     指定生成的model生成所在的包名
            targetProject     指定在该项目下所在的路径
        -->
        <javaModelGenerator targetPackage="com.test.entity"
                            targetProject="src/main/java">

            <!-- 是否允许子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对model添加 构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否对类CHAR类型的列的数据进行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model对象是否 不可改变  即生成的Model对象不会有 setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>

        <!--Mapper映射文件生成所在的目录 为每一个数据库的表生成对应的SqlMap文件 -->
        <sqlMapGenerator targetPackage="com.test.dao.mapper"
                         targetProject="src/main/java">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>

        <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
                type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
                type="MIXEDMAPPER",生成基于注解的Java Model 和相应的Mapper对象
                type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
        -->
        <javaClientGenerator targetPackage="com.test.dao"
                             targetProject="src/main/java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- mybatis.generator1.3.4支持修改mapperName,需要源码calculateMyBatis3XmlMapperFileName修改mapper后缀 -->
        <table tableName="user" domainObjectName="User"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false" mapperName="UserDao">
        </table>

    </context>
</generatorConfiguration>

generatorConfig.xml的配置可以参考《MyBatis从入门到精通》第5章

3.执行mybatis-generator:generate命令即可生成配置的table对应代码

创建一个简单的maven插件

参考maven实战第17章
了解插件的基本实现以及插件的运行入口类对接下来的源码调试修改有所帮助

1.插件本身也是maven项目,区别的地方是打包方式必须为maven-plugin
首先pom.xml需要导入两个依赖:

  • maven-plugin-api包含插件开发必须的类
  • maven-plugin-annotations提供注解支持
  <groupId>com.test</groupId>
  <artifactId>maven-plugin-demo</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>maven-plugin-demo</name>
  <!-- 以maven插件方式打包 -->
  <packaging>maven-plugin</packaging>

  <dependencies>
    <!--插件开发API-->
    <dependency>
      <groupId>org.apache.maven</groupId>
      <artifactId>maven-plugin-api</artifactId>
      <version>3.5.4</version>
    </dependency>
    <!--注解定义-->
    <dependency>
      <groupId>org.apache.maven.plugin-tools</groupId>
      <artifactId>maven-plugin-annotations</artifactId>
      <version>3.5.2</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

2.为插件编写目标:创建一个类继承AbstractMojo并实现execute()方法,Maven称为Mojo(maven old java object与Pojo对应),实际上我们执行插件命令时会执行对应的Mojo中的execute()方法

@Mojo(name = "hi")
public class Helloworld extends AbstractMojo {
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        System.out.println("Hello World!");
    }
}

@Mojo(name = "hi")定义了插件的目标名称,执行插件时通过groupId:artifactId:version:名称,例如:上面我们定义的插件执行命令为com.test:maven-plugin-demo:1.0-SNAPSHOT:hi
其他的注解还有@Parameter用于读取参数配置等

3.使用插件

  • 首先我们把创建的自定义插件项目install到本地仓库
  • 然后其他项目就可以在pom.xml中使用<plugin>标签引入插件了
<build>
    <plugins>
        <!-- 使用自定义插件-->
        <plugin>
            <groupId>com.test</groupId>
            <artifactId>maven-plugin-demo</artifactId>
            <version>1.0-SNAPSHOT</version>
        </plugin>
    </plugins>
</build>

IntelliJIdea可以在Maven Projects插件栏看到我们引入的插件而直接运行,也可以通过com.test:maven-plugin-demo:1.0-SNAPSHOT:hi命令运行

自定义插件运行结果

插件代码的调试

远程调试步骤:①服务端建立监听②使用相同代码的客户端打断点建立连接并调试

1.maven提供mvnDebug命令行模式启动,默认8000端口号,mvnDebug groupId:artifactId:version:名称,执行mvnDebug com.test:maven-plugin-demo:1.0-SNAPSHOT:hi

启动远程调试

2.可以在当前项目下通过remote连接,module选择当前插件项目
remote配置

3.然后就可以打断点debug了
debug

调试MBG并修改源码实现我们想要的效果(接口Dao结尾xml以Mapper结尾)

1.到github下载源码https://github.com/mybatis/generator/releases,这里我下载的是1.3.4的Source code(zip),在IDEA中打开项目,结构如下:

MBG项目结构

2.比较重要的是plugin和core两个工程,而且plugin依赖core工程
plugin依赖core

3.在plugin工程中可以找到以Mojo结尾的项目入口类,那么我们就可以在execute()打上断点调试

@Mojo(name = "generate",defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class MyBatisGeneratorMojo extends AbstractMojo { 
    public void execute() throws MojoExecutionException {
    {
        //......
        try {
            ConfigurationParser cp = new ConfigurationParser(
                    project.getProperties(), warnings);
            /**
             * 解析后返回Configuration对象,对应XML中的generatorConfiguration根标签
             * Configuration对象中的List<Context> contexts对象则对应XML中配置的多个context标签
             * Context类对象中的ArrayList<TableConfiguration> tableConfigurations则对应XML配置的多个table标签
             * 根据它们之间的包含关系,可以看到TableConfiguration类中就有mapperName属性
             */
            Configuration config = cp.parseConfiguration(configurationFile);
            // ShellCallback作用于IDE执行环境的支持:主要是文件创建,已存在文件时是否支持覆盖,java文件支持合并,以及文件创建完成提醒IDE刷新project
            ShellCallback callback = new MavenShellCallback(this, overwrite);
            
            MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
                    callback, warnings);
            /**
             * 执行generate生成mapper
             * MavenProgressCallback:log日志打印执行过程,verbose:默认false不打印
             * contextsToRun:参数配置,限制哪些context应该被执行
             * fullyqualifiedTables:参数配置,限制哪些table应该被生成
             */
            myBatisGenerator.generate(new MavenProgressCallback(getLog(),
                    verbose), contextsToRun, fullyqualifiedTables);

        } catch (XMLParserException e) {
            for (String error : e.getErrors()) {
                getLog().error(error);
            }

            throw new MojoExecutionException(e.getMessage());
        }
        //......
    }
}
  • Configuration config = cp.parseConfiguration(configurationFile);首先是generatorConfig.xml配置文件的加载和解析,点进方法里边可以看到是使用Document读取XML文档的方式,也就是需要解析到我们的配置文件

涉及到XML配置的首先想到都是要先读取解析XML,我们在《Spring源码深度解析》、《MyBatis技术内幕》都可以看到先从XML配置文件的解析开始

使用Document读取XML文档的简单流程:

//使用DocumentBuilder 
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
//得到Document对象, builder.parse可以接收InputStream/file或者url
Document doc = builder.parse(file);
Element root = doc.getDocumentElement();//获取root根节点对象
NodeList nodelist = root.getChildNodes();
  • 接下来我们进入parseConfiguration()方法,可以发现他把解析工作交给了MyBatisGeneratorConfigurationParser类去解析:
public class ConfigurationParser { 
    private Configuration parseConfiguration(InputSource inputSource) throws IOException, XMLParserException {
        //......略
        Configuration config;
        Element rootNode = document.getDocumentElement();
        DocumentType docType = document.getDoctype();
        if (rootNode.getNodeType() == Node.ELEMENT_NODE
                && docType.getPublicId().equals(
                        XmlConstants.IBATOR_CONFIG_PUBLIC_ID)) {
            config = parseIbatorConfiguration(rootNode);
        } else if (rootNode.getNodeType() == Node.ELEMENT_NODE
                && docType.getPublicId().equals(
                        XmlConstants.MYBATIS_GENERATOR_CONFIG_PUBLIC_ID)) {
            //DTD文档PUBLIC:根据generatorconfig.xml的文档头部定义的PUBLIC区分使用MyBatis文档方式解析      
            config = parseMyBatisGeneratorConfiguration(rootNode);
        } else {
            throw new XMLParserException(getString("RuntimeError.5")); //$NON-NLS-1$
        }
        return config;
    }
    private Configuration parseMyBatisGeneratorConfiguration(Element rootNode)
        throws XMLParserException {
    MyBatisGeneratorConfigurationParser parser = new MyBatisGeneratorConfigurationParser(
            extraProperties);
            //继续执行解析操作
    return parser.parseConfiguration(rootNode);
    }
}
  • 进到MyBatisGeneratorConfigurationParserparseConfiguration方法,可以发现是一层层的标签解析并封装到Configuration类对应的属性中,我们可以通过以下顺序找到mapperName
    parseConfiguration()->parseContext()->parseTable()
protected void parseTable(Context context, Node node) {
    TableConfiguration tc = new TableConfiguration(context);
    context.addTableConfiguration(tc);
    //获取mapperName属性并设置到TableConfiguration对象中
    String mapperName = attributes.getProperty("mapperName");
    if (stringHasValue(mapperName)) {
        tc.setMapperName(mapperName);
    }
}

通过Configuration config = cp.parseConfiguration(configurationFile);我们了解到XML配置文件会解析封装为Configuration对象,而且也找到了解析读取mapperName属性的地方

  • 接下来可以看执行生成的主流程myBatisGenerator.generate(new MavenProgressCallback(getLog(), verbose), contextsToRun, fullyqualifiedTables);
public class MyBatisGenerator {
    public void generate(ProgressCallback callback, Set<String> contextIds,
            Set<String> fullyQualifiedTableNames, boolean writeFiles) throws SQLException,
            IOException, InterruptedException {
        //......
        // now run the introspections...
        for (Context context : contextsToRun) {
            //连接数据库并读取保存table信息,等待后面的generateFiles生成文件
            context.introspectTables(callback, warnings,
                    fullyQualifiedTableNames);
        }

        // now run the generates
        for (Context context : contextsToRun) {
            //生成GeneratedJavaFile/GeneratedXmlFile对象,用于后面生成文件
            context.generateFiles(callback, generatedJavaFiles,
                    generatedXmlFiles, warnings);
        }
        // now save the files
        if (writeFiles) {
            callback.saveStarted(generatedXmlFiles.size()
                + generatedJavaFiles.size());

            for (GeneratedXmlFile gxf : generatedXmlFiles) {
                projects.add(gxf.getTargetProject());
                writeGeneratedXmlFile(gxf, callback);
            }

            for (GeneratedJavaFile gjf : generatedJavaFiles) {
                projects.add(gjf.getTargetProject());
                //获取java文件内容source = gjf.getFormattedContent()可以看interfaze类中拼接内容的方法
                writeGeneratedJavaFile(gjf, callback);
            }

            for (String project : projects) {
                shellCallback.refreshProject(project);
            }
        }
        callback.done();
    }
}
  • 其中先通过introspectTables方法获取表的信息,然后再执行generateFiles生成GeneratedJavaFile保存要生成的文件结构,然后再通过writeGeneratedJavaFile获取文件内容以及编码等信息在目录下生成文件。

表示对IntrospectedTable表示不太理解,搜了一篇介绍IntrospectedTable是提供扩展的基础类,配置文件context标签上设置的runtime对应的就是不同的IntrospectedTable的实现,接下来我们观察代码时也会看到这点。

  • 先看context.introspectTables方法,里边主要是获取了数据库连接Connection,以及调用List<IntrospectedTable> tables = databaseIntrospector .introspectTables(tc);方法:
public class DatabaseIntrospector {
    public List<IntrospectedTable> introspectTables(TableConfiguration tc)
            throws SQLException {
        // 获取列信息
        Map<ActualTableName, List<IntrospectedColumn>> columns = getColumns(tc);

        removeIgnoredColumns(tc, columns);
        calculateExtraColumnInformation(tc, columns);
        applyColumnOverrides(tc, columns);
        calculateIdentityColumns(tc, columns);

        List<IntrospectedTable> introspectedTables = calculateIntrospectedTables(
                tc, columns);
        // ......略
        return introspectedTables;
    }
    private List<IntrospectedTable> calculateIntrospectedTables(
            TableConfiguration tc,
            Map<ActualTableName, List<IntrospectedColumn>> columns) {
        boolean delimitIdentifiers = tc.isDelimitIdentifiers()
                || stringContainsSpace(tc.getCatalog())
                || stringContainsSpace(tc.getSchema())
                || stringContainsSpace(tc.getTableName());

        List<IntrospectedTable> answer = new ArrayList<IntrospectedTable>();

        for (Map.Entry<ActualTableName, List<IntrospectedColumn>> entry : columns
                .entrySet()) {
            ActualTableName atn = entry.getKey();
            //过滤一些没有指定的不必要的信息
            FullyQualifiedTable table = new FullyQualifiedTable(
                    //......略
                    delimitIdentifiers, context);
            //创建IntrospectedTable并返回
            IntrospectedTable introspectedTable = ObjectFactory
                    .createIntrospectedTable(tc, table, context);

            for (IntrospectedColumn introspectedColumn : entry.getValue()) {
                introspectedTable.addColumn(introspectedColumn);
            }
            calculatePrimaryKey(table, introspectedTable);
            enhanceIntrospectedTable(introspectedTable);
            answer.add(introspectedTable);
        }
        return answer;
    }
}
  • 可以看到,getColumns(tc)方法通过访问数据库获取到列信息,然后可以发现createIntrospectedTable创建IntrospectedTable的方法:
public class ObjectFactory {
    public static IntrospectedTable createIntrospectedTable(
            TableConfiguration tableConfiguration, FullyQualifiedTable table,
            Context context) {
        IntrospectedTable answer = createIntrospectedTableForValidation(context);
        answer.setFullyQualifiedTable(table);
        answer.setTableConfiguration(tableConfiguration);
        return answer;
    }
    public static IntrospectedTable createIntrospectedTableForValidation(Context context) {
        String type = context.getTargetRuntime();
        if (!stringHasValue(type)) {
            type = IntrospectedTableMyBatis3Impl.class.getName();
        } else if ("Ibatis2Java2".equalsIgnoreCase(type)) { //$NON-NLS-1$
            type = IntrospectedTableIbatis2Java2Impl.class.getName();
        } else if ("Ibatis2Java5".equalsIgnoreCase(type)) { //$NON-NLS-1$
            type = IntrospectedTableIbatis2Java5Impl.class.getName();
        } else if ("Ibatis3".equalsIgnoreCase(type)) { //$NON-NLS-1$
            type = IntrospectedTableMyBatis3Impl.class.getName();
        } else if ("MyBatis3".equalsIgnoreCase(type)) { //$NON-NLS-1$
            type = IntrospectedTableMyBatis3Impl.class.getName();
        } else if ("MyBatis3Simple".equalsIgnoreCase(type)) { //$NON-NLS-1$
            type = IntrospectedTableMyBatis3SimpleImpl.class.getName();
        }
        IntrospectedTable answer = (IntrospectedTable) createInternalObject(type);
        answer.setContext(context);
        return answer;
    }
}

createIntrospectedTableForValidation方法中通过runtime的设置,会使用不同的IntrospectedTable实现,我们之前配置文件中的是targetRuntime="MyBatis3",对应会使用IntrospectedTableMyBatis3Impl这个实现类,接下来的generateFiles流程就是用的IntrospectedTableMyBatis3Impl里边的方法

  • context.generateFiles生成GeneratedJavaFile/GeneratedXmlFile对象,用于后面生成文件,可以说这里边即将构建生成的就是最终的文件结构,后面的writeFile生成文件也只是读取里边的信息生成文件而已:
public void generateFiles(ProgressCallback callback,
        List<GeneratedJavaFile> generatedJavaFiles,
        List<GeneratedXmlFile> generatedXmlFiles, List<String> warnings)
        throws InterruptedException {
    //......略
    if (introspectedTables != null) {
        for (IntrospectedTable introspectedTable : introspectedTables) {
            callback.checkCancel();
            //这里的initialize/calculateGenerators/getGeneratedJavaFiles方法都是调用runtime对应实现类里边的方法
            introspectedTable.initialize();
            introspectedTable.calculateGenerators(warnings, callback);
            generatedJavaFiles.addAll(introspectedTable
                    .getGeneratedJavaFiles());
            generatedXmlFiles.addAll(introspectedTable
                    .getGeneratedXmlFiles());

            generatedJavaFiles.addAll(pluginAggregator
                    .contextGenerateAdditionalJavaFiles(introspectedTable));
            generatedXmlFiles.addAll(pluginAggregator
                    .contextGenerateAdditionalXmlFiles(introspectedTable));
        }
    }
    generatedJavaFiles.addAll(pluginAggregator
            .contextGenerateAdditionalJavaFiles());
    generatedXmlFiles.addAll(pluginAggregator
            .contextGenerateAdditionalXmlFiles());
}
  • 主要分析里边调用的IntrospectedTableMyBatis3Impl的3个方法(initialize,calculateGenerators,getGeneratedJavaFiles
    首先是初始化initialize
public void initialize() {
    //设置java客户端接口的属性
    calculateJavaClientAttributes();
    //设置model实体类的属性
    calculateModelAttributes();
    //设置XML
    calculateXmlAttributes();
    //......
}
protected void calculateJavaClientAttributes() {
    //......
    sb.setLength(0);
    sb.append(calculateJavaClientInterfacePackage());
    sb.append('.');
    sb.append(fullyQualifiedTable.getDomainObjectName());
    sb.append("DAO"); //$NON-NLS-1$
    setDAOInterfaceType(sb.toString());//DAO接口的名称!

    sb.setLength(0);
    sb.append(calculateJavaClientInterfacePackage());
    sb.append('.');
    if (stringHasValue(tableConfiguration.getMapperName())) {//设置了Mapper
        sb.append(tableConfiguration.getMapperName());
    } else {
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("Mapper"); //$NON-NLS-1$
    }
    setMyBatis3JavaMapperType(sb.toString());
}

这里我们可以发现Mapper的设置,以及产生一个疑问:DAOInterfaceType明明单独设置了接口是DAO为什么生成的时候却变成跟下面的Mapper同样的结尾?

  • 接着看calculateGenerators方法
public class IntrospectedTableMyBatis3Impl extends IntrospectedTable {
    @Override
    public void calculateGenerators(List<String> warnings, ProgressCallback progressCallback) {
        //生成javaClientGenerator
        AbstractJavaClientGenerator javaClientGenerator = calculateClientGenerators(warnings, progressCallback);
    }
    protected AbstractJavaClientGenerator calculateClientGenerators(List<String> warnings, ProgressCallback progressCallback) {
        AbstractJavaClientGenerator javaGenerator = createJavaClientGenerator();
        return javaGenerator;
    }
    protected AbstractJavaClientGenerator createJavaClientGenerator() {
        if (context.getJavaClientGeneratorConfiguration() == null) {
            return null;
        }
        String type = context.getJavaClientGeneratorConfiguration()
                .getConfigurationType();

        AbstractJavaClientGenerator javaGenerator;
        if ("XMLMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
            javaGenerator = new JavaMapperGenerator();
        } else if ("MIXEDMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
            javaGenerator = new MixedClientGenerator();
        } else if ("ANNOTATEDMAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
            javaGenerator = new AnnotatedClientGenerator();
        } else if ("MAPPER".equalsIgnoreCase(type)) { //$NON-NLS-1$
            javaGenerator = new JavaMapperGenerator();
        } else {
            javaGenerator = (AbstractJavaClientGenerator) ObjectFactory
                    .createInternalObject(type);
        }
        return javaGenerator;
    }
}

javaClientGenerator标签配置客户端代码,我们使用的是XMLMAPPER单独生成XML和接口文件的方式,对应代码这里会使用JavaMapperGenerator这个生成器,在getGeneratedJavaFiles方法中我们主要看到调用javaGenerator.getCompilationUnits();

public class JavaMapperGenerator extends AbstractJavaClientGenerator {
    @Override
    public List<CompilationUnit> getCompilationUnits() {
        progressCallback.startTask(getString("Progress.17", //$NON-NLS-1$
                introspectedTable.getFullyQualifiedTable().toString()));
        CommentGenerator commentGenerator = context.getCommentGenerator();
        //使用的是MyBatis3JavaMapperType,而不是DAOInterfaceType
        FullyQualifiedJavaType type = new FullyQualifiedJavaType(
                introspectedTable.getMyBatis3JavaMapperType());
        //......
        return answer;
    }
}

这里发现在java客户端代码生成器里边统一使用的都是MyBatis3JavaMapperType,猜测是这里写死了值导致的,代码改成introspectedTable.getDAOInterfaceType()后再install执行插件,果然变成了DAO结尾:

修改为DAOInterfaceType后

我们也可以通过writeGeneratedJavaFile生成文件时获取文件名的方法找到原因:
public String getFileName() {return compilationUnit.getType().getShortNameWithoutTypeArguments() + ".java";}
调用FullyQualifiedJavaTypebaseShortName属性,就是上面通过构造方法传参解析出来的

  • 最后了解了MyBatis Generator的工作流程我们也可以参考有mapperName的地方添加多一个daoName,实现接口文件和xml文件各自的自定义属性:
    ①最开始解析XML的parseTable添加String daoName = attributes.getProperty("daoName");
    introspectedTable.initialize();中的calculateJavaClientAttributes方法参考Mapper修改DAO
    ③给本地项目中XML文档也添加个daoName属性resources\org\mybatis\generator\config\xml\mybatis-generator-config_1_0.dtd
    ④install后执行效果:
    添加自定义daoName属性

总结补充:修改core项目源码后调试时需要重新install一次,不然远程调试不会生效

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容