(快速入门)MyBatis Generator源码分析修改和自定义插件

MyBatis Generator 代码自动生成器 Mybatis的逆向工程就是由代码生成器生成我们需要的代码和映射文件。我们在编写Mybatis程序时,基本都是围绕着pojo类,Mapper接口,Mapper.xml文件等文件来进行的。如果实际开发中数据库的表特别多,那么我们需要手动去写每一张表的pojo类,Mapper接口,Mapper.xml文件,这显然需要花费巨大的精力,而且可能由于表字段太多,哪里写错了都难以排除。所以我们在实际开发中,一般使用逆向工程方式来自动生成所需的文件,这也是企业中一种非常常见的方法。 阅读详情

这里写图片描述

MyBatis Generator 能快速代码生成工具,尽管已经提供了大量的配置标签,但是每个公司都有自己的代码规范,这个时候,别人提供的不合适,那就只能自己上手了,毕竟适合自己的才是最顺手的。

可能是优快云问题,复制的xml粘贴到网页里后,每个标签自动闭合了,结尾自动带了”/”,复制代码时请注意。


快速开始

考虑到有的读者没有使用过,本章为了快速回顾MyBatis Generator的使用,已经会用请自行跳过。

环境配置

基础JAVA开发环境请自行配置好,这里不多做阐述。同时,从官网的GitHub上拉取源码,点击这里 ;或者从下方直接copy仓库地址拉取。

https://github.com/mybatis/generator.git
 
  • 1

当拉取完毕的目录监下图,核心的目录就是core,后续的修改都是基于它开始的。
目录结构

生成代码

再生产代码前有以下几个步骤要做:
创建一张表
这里新建了一个test库,同时加入了一张hello表。

CREATE TABLE `hello` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `code` smallint(6) DEFAULT NULL COMMENT '状态码',
  `msg` mediumtext COMMENT '返回结果',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

新建配置文件
在项目中建立一个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>
  <classPathEntry location="D:\IDEA\generator\core\mybatis-generator-core\src\test\resources\mysql-connector-java-5.1.36.jar" />
  <context id="XHTables" targetRuntime="MyBatis3">

    <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
    <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />
    <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />

    <commentGenerator>
        <property name="suppressDate" value="true" />
          <!-- 是否[去除]自动生成的注释  true:是 :   false:否 -->   
        <property name="suppressAllComments" value="false" />
        <property name="addRemarkComments" value="true" />
    </commentGenerator>
    <!--数据库连接-->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://106.14.216.248:3306/test?useUnicode=true&amp;characterEncoding=UTF-8"
        userId="root"
        password="meiyoumima123">
    </jdbcConnection>
    <!--model 和 example位置-->
    <javaModelGenerator targetPackage="dto" targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </javaModelGenerator>
    <!--mapping文件位置-->
    <sqlMapGenerator targetPackage="mapping"  targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
    <!--XML位置-->
    <javaClientGenerator type="XMLMAPPER" targetPackage="dao" targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>
    <!--要生成的表名-->
    <table tableName="hello" domainObjectName="Hello"
           enableCountByExample="true"
           enableUpdateByExample="true"
           enableDeleteByExample="true"
           enableSelectByExample="true"
           selectByExampleQueryId="false">
      <generatedKey column="ID" sqlStatement="MYSQL" identity="true" />
    </table>
  </context>
</generatorConfiguration>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

运行
平时我们使用插件时,直接使用的是jar包,运行的时候是敲击命令生成:

java -jar mybatis-generator-core-1.3.7.jar -configfile hello.xml -overwrite
 
  • 1

因为,我们这里是分析源码,我们需要知道程序入口,那么以DOS下敲java命令这个线索我们去寻找主入口。
解压jar文件(jar插件通过maven依赖可以直接得到,或者自行下载),我们找到META-INF/MANIFEST.MF(此文件作用请自行百度)文件中找到这么一句:Main-Class: org.mybatis.generator.api.ShellRunner,没错,这就是程序入口,见下图:
main
那么,回到项目,搜索这个类,见下图:
shellRun
同理,我们再启动Main方法之前配置(不会配的话,测试时可以先写死在main方法里)传入参数:

-configfile D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\Hello.xml 
-overwrite 
-verbose
 
  • 1
  • 2
  • 3

其中,-verbose 是打印执行过程,不加则没有。在ShellRunner中对应的是下面这一行代码:

ProgressCallback progressCallback = arguments.containsKey(VERBOSE) ? new VerboseProgressCallback()
                    : null;
 
  • 1
  • 2

如果,配置无误并正确执行的话,将在控制台看到以下信息:

Connecting to the Database
Introspecting table hello
Generating Example class for table hello
Generating Record class for table hello
Generating Mapper Interface for table hello
Generating SQL Map for table hello
Saving file HelloMapper.xml
Saving file HelloExample.java
Saving file Hello.java
Saving file HelloMapper.java
MyBatis Generator finished successfully.
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

生成的目录如下:
目录
如此,所有操作便完成了。预想了解更多,请百度,下边将进入分析源码和插修改源码环节。

源码修改

现在用Maven管理项目是一件很普遍的事,公司对于代码管理有严格的要求,对外提供服务的只能是API(微服务一般是这样),与API(对外提供服务)无关的那么是不允许对外提供访问方式的。
目录
比如,现在有(A、B)2个模块,这里对外提供的A模块只能有Model(这里对应Hello.java),其他的文件(HelloMapper.java、HelloExample.java、HelloMapper.xml)都只能在B的模块中,那么Gennerator可以修改dao和mapping文件目录为B的目录,重新生成即可。但是HelloExample却不能通过配置去做,因为源码是将Hello和HelloExample绑定在了一起了,这时就要靠我们自己来实现这个功能了。

注:有同学说,我手动将HelloExample从A复制到B不就可以了么,干嘛要这么麻烦,如果这样想,也没问题,这位同学你可以离开了(原因,自己悟)。

修改配置文件

<!--model 和 example位置-->
<javaModelGenerator >....省略...</javaModelGenerator>
<!--mapping文件位置-->
<sqlMapGenerator>....省略...</sqlMapGenerator>
<!--XML位置-->
<javaClientGenerator >....省略.../javaClientGenerator>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

我们看配置文件发现既然少了一个配置标签,那么我们就先新增一个标签,复制一个“javaModelGenerator ”标签,并将名字改为“javaExampleGenerator ”:

<?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>
  <classPathEntry location="D:\IDEA\generator\core\mybatis-generator-core\src\test\resources\mysql-connector-java-5.1.36.jar" />
  <context id="XHTables" targetRuntime="MyBatis3">

    <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
    <plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />
    <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />

    <commentGenerator>
        <property name="suppressDate" value="true" />
          <!-- 是否[去除]自动生成的注释  true:是 :   false:否 -->   
        <property name="suppressAllComments" value="false" />
        <property name="addRemarkComments" value="true" />
    </commentGenerator>
    <!--数据库连接-->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://106.14.216.248:3306/test?useUnicode=true&amp;characterEncoding=UTF-8"
        userId="root"
        password="meiyoumima123">
    </jdbcConnection>
    <!--model 和 example位置-->
    <javaModelGenerator targetPackage="dto" targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </javaModelGenerator>
    <!-- 新加入标签,将生成的 Example文件放到指定包下,官方是和 javaModelGenerator 在一个包下,不支持单独配置-->
    <javaExampleGenerator targetPackage="exampleModel" targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </javaExampleGenerator>
    <!--mapping文件位置-->
    <sqlMapGenerator targetPackage="mapping"  targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>
    <!--XML位置-->
    <javaClientGenerator type="XMLMAPPER" targetPackage="dao" targetProject="D:\IDEA\generator1\generator-master\core\mybatis-generator-core\src\test\resources\test">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>
    <!--要生成的表名-->
    <table tableName="hello" domainObjectName="Hello"
           enableCountByExample="true"
           enableUpdateByExample="true"
           enableDeleteByExample="true"
           enableSelectByExample="true"
           selectByExampleQueryId="false">
      <generatedKey column="ID" sqlStatement="MYSQL" identity="true" />
    </table>
  </context>
</generatorConfiguration>
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

如此,配置文件修改完毕,如果IDE启用验证XML个是的话,这里加入完毕后会显示报错,因为DTD验证(自行百科)不通过,我们后续还有修改.dtd文件。不过,我们既然分析源码,那就一步一步来,先不管这个报错。

加入解析器

/**
* 初始化配置解析器
*/
ConfigurationParser cp = new ConfigurationParser(warnings);
/**
* 调用配置解析器创建配置对象
*
*/
Configuration config = cp.parseConfiguration(configurationFile);
/**
* shellcallback接口主要用来处理文件的创建和合并,传入overwrite参数;默认的shellcallback是不支持文件合并的;
*/
DefaultShellCallback shellCallback = new DefaultShellCallback(
arguments.containsKey(OVERWRITE));
/**
* 创建一个MyBatisGenerator对象。MyBatisGenerator类是真正用来执行生成动作的类
*/
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, shellCallback, warnings);
/**
* -verbose 用System.out打印执行过程
*
*/
ProgressCallback progressCallback = arguments.containsKey(VERBOSE) ? new VerboseProgressCallback(): null;
/**
*  执行生成操作,contexts和fullyqualifiedTables都是入口进来的参数,自行百度。
*/
myBatisGenerator.generate(progressCallback, contexts, fullyqualifiedTables);
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

如上,在ShellRunner中这里使我们关注的焦点,开始跟踪代码执行过程,从 Configuration config = cp.parseConfiguration(configurationFile) 一路跟踪到parseConfiguration(InputSource inputSource)
throws IOException, XMLParserException这个方法:

 Configuration config = cp.parseConfiguration(configurationFile);

 
  • 1
  • 2

为了代码证件,这里只显示关键代码:

 private Configuration parseConfiguration(InputSource inputSource)
            throws IOException, XMLParserException {
             //1 设置校验的dtd资源文件
             builder.setEntityResolver(new ParserEntityResolver());
             //2 解析配置
             config = parseMyBatisGeneratorConfiguration(rootNode);

    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
修改DTD校验文件

先看标识1的代码,ParserEntityResolver 是用来做dtd验证,进入这个对象,并查看下面这个方法resolveEntity(String publicId, String systemId),会找到这一句:

    InputStream is = getClass()getClassLoader().getResourceAsStream("org/mybatis/generator/config/xml/mybatis-generator-config_1_0.dtd"); 
    InputSource ins = new InputSource(is);

 
  • 1
  • 2

因为我们配置的hello.xml文件头是这个,和判断条件一致:

dtd文件

所有,它读的dtd文件路径知道了:
文件位置以org/mybatis/generator/config/xml/mybatis-generator-config_1_0.dtd,剩下的就是修改这个文件,复制javaModelGenerator,将复制的这个改下节点名字为javaExampleGenerator,如图操作:
复制节点
还没完,这个是回去看hello.xml,如果开了验证的话,发现还是在报错,因为还有一个地方:
dtd头
同样加入新增的javaExampleGenerator节点,这时再回去hello.xml看就不报错了。

新建解析器

回到ConfigurationParser.parseConfiguration(InputSource inputSource)的这个方法。

 private Configuration parseConfiguration(InputSource inputSource)
            throws IOException, XMLParserException {
             //1 设置校验的dtd资源文件
             builder.setEntityResolver(new ParserEntityResolver());
             //2 解析配置
             config = parseMyBatisGeneratorConfiguration(rootNode);

    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

刚才,分析了1的方法,和dtd一样,因为hello.xml的头我们已经固定了,所以这里用的是parseMyBatisGeneratorConfiguration(*)这个方法,现在找到2这一行继续跟踪:

private Configuration parseMyBatisGeneratorConfiguration(Element rootNode)
            throws XMLParserException {
        MyBatisGeneratorConfigurationParser parser = new MyBatisGeneratorConfigurationParser(
                extraProperties);
        return parser.parseConfiguration(rootNode);
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

发现到这里换成了MyBatisGeneratorConfigurationParser的解析器,继续:

public Configuration parseConfiguration(Element rootNode)
            throws XMLParserException {
      ...
      parseContext(configuration, childNode);
      ...
      return configuration;
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

通过hello.xml发现,不论是javaModelGenerator标签还是我们新加的javaExampleGenerator都是context的子标签,由parseContext继续往下:

private void parseContext(Configuration configuration, Node node) {
            ...

             if ("property".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parseProperty(context, childNode);
            } else if ("plugin".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parsePlugin(context, childNode);
            } else if ("commentGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parseCommentGenerator(context, childNode);
            } else if ("jdbcConnection".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parseJdbcConnection(context, childNode);
            } else if ("connectionFactory".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parseConnectionFactory(context, childNode);
            } else if ("javaModelGenerator".equals(childNode.getNodeName())) { //$NON-NLS-1$
                parseJavaModelGenerator(context, childNode);
}

        ...
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这里发现,它根据不同标签进行解析,以“parseJavaModelGenerator”为例,查看parseJavaModelGenerator(context, childNode)这个方法:

 protected void parseJavaModelGenerator(Context context, Node node) {
        //1 自定义解析器
        JavaModelGeneratorConfiguration javaModelGeneratorConfiguration = new JavaModelGeneratorConfiguration(); 
        // 2 将解析器放入上下文
        context.setJavaModelGeneratorConfiguration(javaModelGeneratorConfiguration);
        }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

不难看出,核心方法有2个,那就照着复制修改呗:
复制
先定位JavaModelGeneratorConfiguration类,复制一份修改类名为JavaExampleGeneratorConfiguration,并将toXmlElement里的节点名字换成我们新加入的标签。
这里只是加入了解析器,我们需要用上我们的解析器,并且把解析到的内容放入到上下文中,因此:
1、复制parseJavaModelGenerator方法并改名为parseJavaExampleGenerator,然后应用我们的解析器:
解析器

上边是应用了解析器,但是我们的上下文还没有放解析的内容,注意紫色框框。

2、紫色框框里的Context对象是存放上下文信息的,我们将解析器加入到Context中,并生成getter/setter:

private JavaExampleGeneratorConfiguration javaExampleGeneratorConfiguration;
 public JavaExampleGeneratorConfiguration getJavaExampleGeneratorConfiguration() {
        return javaExampleGeneratorConfiguration;
    }

    public void setJavaExampleGeneratorConfiguration(JavaExampleGeneratorConfiguration javaExampleGeneratorConfiguration) {
        this.javaExampleGeneratorConfiguration = javaExampleGeneratorConfiguration;
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3、让DOM读到新加标签时,交给我们的解析器处理,因此还需要在parseContext(Configuration configuration, Node node)中加入我们的分支节点:
example
至此,我们新加入的标签已经可以正确解析,数据准备阶段已完成。下一步要做的是让生成工具能读到我们新加的解析器,并使用其中的信息。

获取表数据

前期的准备工作已完成,现在开始从生成代码开始往下跟,回到ShellRunner,以下面的generate(*)往下跟踪代码:

 myBatisGenerator.generate(progressCallback, contexts, fullyqualifiedTables);
 
  • 1

进入代码块:

public void generate(ProgressCallback callback, Set<String> contextIds,
            Set<String> fullyQualifiedTableNames, boolean writeFiles) throws SQLException, IOException, InterruptedException {
            ...
        //0、 连接数据库,解析表字段
        for (Context context : contextsToRun) {
            context.introspectTables(callback, warnings,fullyQualifiedTableNames);
        }
        //1、生成JAVA和xml对应资源文件
        for (Context context : contextsToRun) {
            context.generateFiles(callback, generatedJavaFiles,generatedXmlFiles, warnings);
        }
        //2、保存文件
        if (writeFiles) {
                    ...
           }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

先看连接数据库:

public void introspectTables(ProgressCallback callback,
            List<String> warnings, Set<String> fullyQualifiedTableNames)
            throws SQLException, InterruptedException {
            // 初始化表数组
            introspectedTables = new ArrayList<IntrospectedTable>();
            // 获取表信息
            for (TableConfiguration tc : tableConfigurations) {
             ...   
             List<IntrospectedTable> tables = databaseIntrospector.introspectTables(tc);
             ...
            }
      }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

通过databaseIntrospector.introspectTables(tc);往下追踪:

public List<IntrospectedTable> introspectTables(TableConfiguration tc)
            throws SQLException {
            List<IntrospectedTable> introspectedTables = calculateIntrospectedTables(
                tc, columns)
}

private List<IntrospectedTable> calculateIntrospectedTables(
            TableConfiguration tc,
            Map<ActualTableName, List<IntrospectedColumn>> columns) {
            IntrospectedTable introspectedTable = ObjectFactory
                    .createIntrospectedTable(tc, table, context);
           }
public static IntrospectedTable createIntrospectedTable(
            TableConfiguration tableConfiguration, FullyQualifiedTable table,
            Context context) {

        IntrospectedTable answer = createIntrospectedTableForValidation(context);
}
 public static IntrospectedTable createIntrospectedTableForValidation(Context context) {
     String type = context.getTargetRuntime();
    if ("MyBatis3".equalsIgnoreCase(type)) { 
            type = IntrospectedTableMyBatis3Impl.class.getName();
        }
     //  IntrospectedTable  其实就是 IntrospectedTableMyBatis3Impl对象
     IntrospectedTable answer = (IntrospectedTable) createInternalObject(type);
 }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

注意:IntrospectedTable 其实就是 IntrospectedTableMyBatis3Impl对象,这个后边会用到。

以上代码忽略了其他内容,主要是对查到的表数据进行过滤筛选,组建后续资源文件所需的对象,感兴趣可以自行调试,
这里主要是看到 IntrospectedTableMyBatis3Impl.class这个对象的生成。而这个Type 对应 的是helllo.xml中context标签的“targetRuntime=”MyBatis3”。而每一个IntrospectedTable都封装了对应一个表的全部构建信息。

生成文件基础信息

看完连接数据库,再看第一步“”生成JAVA和XML对应的资源文件“,往下走:

public void generateFiles(ProgressCallback callback,
            List<GeneratedJavaFile> generatedJavaFiles,
            List<GeneratedXmlFile> generatedXmlFiles, List<String> warnings)
            throws InterruptedException {

        //1、插件实例化
        for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
            ...
        }
            ...
        if (introspectedTables != null) {
            for (IntrospectedTable introspectedTable : introspectedTables) {
                callback.checkCancel();
                //2、初始化生成规则、包和表名
                introspectedTable.initialize();
                introspectedTable.calculateGenerators(warnings, callback); // 3、添加解析器参数
                generatedJavaFiles.addAll(introspectedTable.getGeneratedJavaFiles()); // 4、生成Java类 *Example
                generatedXmlFiles.addAll(introspectedTable.getGeneratedXmlFiles()); // 5、生成xml
                ...
            }
        }

    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

上图,第一步插件实例化,这个我们先无需关心,后续我们手写插件的时候,这里才会是重点。看第二步:

public void initialize() {
        calculateJavaClientAttributes(); // mapper
        calculateModelAttributes();// dao、example资源位置这里生成
        calculateXmlAttributes(); // xml
        ...
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里我们主要看 calculateModelAttributes()方法:

 protected void calculateModelAttributes() {
        // 1、读取的是javaModelGenerator 标签的包路径
        String pakkage = calculateJavaModelPackage();

        // 2、拼装全限定名,会影响生成Hello的位置
        sb.setLength(0);
        sb.append(pakkage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        setBaseRecordType(sb.toString());

         // 2、拼装全限定名,会影响生成HelloExample的位置
        sb.setLength(0);
        sb.append(pakkage);
        sb.append('.');
        sb.append(fullyQualifiedTable.getDomainObjectName());
        sb.append("Example"); //$NON-NLS-1$
        setExampleType(sb.toString());
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

上图,我们看到这里将Hello和HelloExample的全限定类名搞出来了,这个会影响最终文件的位置,然而String pakkage = calculateJavaModelPackage();方法跟踪进去:

 protected String calculateJavaModelPackage() {
        // 获取Context上下文中已经缓存了javaModelGenerator标签属性信息的JavaModelGeneratorConfiguration解析器
        JavaModelGeneratorConfiguration config = context.getJavaModelGeneratorConfiguration();
        StringBuilder sb = new StringBuilder();
        sb.append(config.getTargetPackage());// javaModelGenerator 标签中 targetPackage 属性的值
        sb.append(fullyQualifiedTable.getSubPackageForModel(isSubPackagesEnabled(config)));
        return sb.toString();
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我这里注释的很明白了,我们也应该明白为啥说Hello和HelloExample是绑定在一起了。
如此,找到病根,那就好解决了,既然绑在一起那我们就把它拆开:
1、复制calculateJavaModelPackage()并命名为calculateJavaExamplePackage(),然后重从Context中获取我们自定义解析器的信息,下一步将会用到这个方法:
这里写图片描述

2、复制calculateModelAttributes()并改为 calculateExampleAttributes(),将 calculateExampleAttributes()中拼接HelloExample全限定类名剪贴到calculateExampleAttributes()方法中,注意新方法应用的是我们自己加的解析器即用的是上一步操作中复制的calculateJavaExamplePackage()方法名:
这里写图片描述

生成资源生成器

重新回到生成资源文件这里,刚刚分析完了第二步,现在看第三步,在连接数据库那一步,
我们已经知道IntrospectedTable 其实就是 IntrospectedTableMyBatis3Impl对象,查看calculateGenerators.calculateGenerators()方法:

 @Override
    public void calculateGenerators(List<String> warnings,
            ProgressCallback progressCallback) {
        // 添加Model、Example等生成器
        calculateJavaModelGenerators(warnings, progressCallback);
        // 添加Mapper等生成器
        AbstractJavaClientGenerator javaClientGenerator =
                calculateClientGenerators(warnings, progressCallback);
        // 添加xml生成器
        calculateXmlMapperGenerator(javaClientGenerator, warnings, progressCallback);
    }

protected void calculateJavaModelGenerators(List<String> warnings,
            ProgressCallback progressCallback) {
         // 根据规则判断是否需要Example生成器,即HelloExample
        if (getRules().generateExampleClass()) {
            AbstractJavaGenerator javaGenerator = new ExampleGenerator();
            initializeAbstractGenerator(javaGenerator, warnings,
                    progressCallback);
            javaModelGenerators.add(javaGenerator);
        }
        ...
        //根据规则判断是否需要Model生成器,即生成Hello
        if (getRules().generateBaseRecordClass()) {
            AbstractJavaGenerator javaGenerator = new BaseRecordGenerator();
            initializeAbstractGenerator(javaGenerator, warnings,
                    progressCallback);
            javaModelGenerators.add(javaGenerator);
        }
  }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在calculateJavaModelGenerators()方法中看到构造器都放在javaModelGenerators这个List中,这个会影响到后续获取包路径问题,因此我们要在此类中加入一个我们定义标签的数组:
数组
然后,修改calculateJavaModelGenerators() 方法,将getRules().generateExampleClass()的javaModelGenerators替换成新加的javaExampleGenerators数组中:

if (getRules().generateExampleClass()) {
            AbstractJavaGenerator javaGenerator = new ExampleGenerator();
            initializeAbstractGenerator(javaGenerator, warnings,
                    progressCallback);
            javaExampleGenerators.add(javaGenerator);
        }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

保存资源文件信息

看完第三步,在看第四步:

public void generateFiles(ProgressCallback callback,
            List<GeneratedJavaFile> generatedJavaFiles,
            List<GeneratedXmlFile> generatedXmlFiles, List<String> warnings)
            throws InterruptedException {

        //1、插件实例化
        for (PluginConfiguration pluginConfiguration : pluginConfigurations) {
            ...
        }
            ...
        if (introspectedTables != null) {
            for (IntrospectedTable introspectedTable : introspectedTables) {
                callback.checkCancel();
                //2、初始化生成规则、包和表名
                introspectedTable.initialize();
                introspectedTable.calculateGenerators(warnings, callback); // 3、添加解析器参数
                generatedJavaFiles.addAll(introspectedTable.getGeneratedJavaFiles()); // 4、生成Java类 *Example
                generatedXmlFiles.addAll(introspectedTable.getGeneratedXmlFiles()); // 5、生成xml
                ...
            }
        }

    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

第四步的introspectedTable.getGeneratedJavaFiles()方法,在连接数据库那一步,
我们已经知道IntrospectedTable 其实就是 IntrospectedTableMyBatis3Impl对象,那么我们看看IntrospectedTableMyBatis3Impl.getGeneratedJavaFiles()的实现:

这里写图片描述
在第三步分析的时候已经将HelloExample文件从javaModelGenerators中单独抽离了,我们是放在javaExampleGenerators中,如果不抽离出来在这里可以明确看出Hello和HelloExample都在javaModelGenerators中,获取的都是javaModelGenerator解析器里存的包名,即对应的还是javaModelGenerator标签的值,而不是我们自定义的。
改动如下:
这里写图片描述

最后,循环保存文件,这里就不多阐述了。
至此,源码修改完毕了,我们运行下ShellRunner来看下吧!
这里写图片描述

大功告成,快打包成jar试试看吧!

自定义插件KryoPlugin

hello

上图,是我们生成的Hello文件,我们已经看到它已经实现了序列化、重新HashCode、toString,这是因为我们在配置文件有这么几个配置:

<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />
<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
 
  • 1
  • 2
  • 3

他们分别是启用了已有的序列化插件、HashCode插件、ToString插件。
现在回看Hello.java图片,现在假设我们想给每个生成的Model加上注解,并且写上我们自己的注释,最终的效果:

/**
* Hello 设置Kryo的CompatibleFieldSerializer
* 程序自动生成,无需更改。
*/
@DefaultSerializer(CompatibleFieldSerializer.class)
public class Hello implements Serializable {
    ...
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

就是使用注解的方式使用Kryo的序列化技术,目标明确开始实现。

新建插件

我们先看下SerializablePlugin的写法,我们发现在这包下除了我们配置的ToStringPlugin、EqualsHashCodePlugin、SerializablePlugin插件外,还有其他插件,但他们都有一个共同点都是继承自PluginAdapter,并重写了里面的方法。
如此,先复制SerializablePlugin并重命名为SerializableAnnoPlugin,然后开始实现我们自己的方法:

public class SerializableAnnoPlugin extends PluginAdapter {

    private FullyQualifiedJavaType defaultSerializer;
    private FullyQualifiedJavaType compatibleFieldSerializer;

    public SerializableAnnoPlugin() {
        super();
        defaultSerializer = new FullyQualifiedJavaType("com.esotericsoftware.kryo.DefaultSerializer");
        compatibleFieldSerializer = new FullyQualifiedJavaType("com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer");
    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }

    @Override
    public boolean modelBaseRecordClassGenerated(TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
        makeSerializable(topLevelClass, introspectedTable);
        return true;
    }

    @Override
    public boolean modelPrimaryKeyClassGenerated(TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
        makeSerializable(topLevelClass, introspectedTable);
        return true;
    }

    @Override
    public boolean modelRecordWithBLOBsClassGenerated(
            TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        makeSerializable(topLevelClass, introspectedTable);
        return true;
    }

    protected void makeSerializable(TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
        ...
    }
}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

defaultSerializer 和 compatibleFieldSerializer 分别添加的是全限定类名,这个其实相当于导入jar的操作,会在头部生成:

import com.esotericsoftware.kryo.DefaultSerializer;
import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer;
import java.io.Serializable;
 
  • 1
  • 2
  • 3

makeSerializable()现在是空方法,因此,我们要怎么去写呢,慢慢分析。
1、我们从目录去找到“生成资源生成器”那一部分,即IntrospectedTableMyBatis3Impld的calculateJavaModelGenerators方法,我们找到对应的生成器:

protected void calculateJavaModelGenerators(List<String> warnings,
            ProgressCallback progressCallback) {
        if (getRules().generateExampleClass()) {
            AbstractJavaGenerator javaGenerator = new ExampleGenerator();
            initializeAbstractGenerator(javaGenerator, warnings, progressCallback);
            javaExampleGenerators.add(javaGenerator);
        }

       ...

         if (getRules().generateBaseRecordClass()) {
            AbstractJavaGenerator javaGenerator = new BaseRecordGenerator();
            initializeAbstractGenerator(javaGenerator, warnings, progressCallback);
            javaModelGenerators.add(javaGenerator);
        }
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2、new BaseRecordGenerator();这个就是生成Model的关键操作。在看这类的方法getGeneratedJavaFiles(),这个方法作用前边已经分析过,但注意看javaGenerator.getCompilationUnits()这一句:

public List<GeneratedJavaFile> getGeneratedJavaFiles() {
        List<GeneratedJavaFile> answer = new ArrayList<GeneratedJavaFile>();
        for (AbstractJavaGenerator javaGenerator : javaModelGenerators) {
            List<CompilationUnit> compilationUnits = javaGenerator.getCompilationUnits();
            for (CompilationUnit compilationUnit : compilationUnits) {
                GeneratedJavaFile gjf = new GeneratedJavaFile(compilationUnit, context.getJavaModelGeneratorConfiguration().getTargetProject(), context.getProperty(PropertyRegistry.CONTEXT_JAVA_FILE_ENCODING), context.getJavaFormatter());
                answer.add(gjf);
            }
        }
 }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这个生成器其实是调用BaseRecordGenerator的getCompilationUnits()方法,这里就是生成资源的核心了:

public List<CompilationUnit> getCompilationUnits() {

        CommentGenerator commentGenerator = context.getCommentGenerator();
        ...
        // Model的注释
        commentGenerator.addModelClassComment(topLevelClass, introspectedTable);
        ...
        return answer;
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们看到,先从上下文拿到构造器context.getCommentGenerator()。然后进入 commentGenerator.addModelClassComment(topLevelClass, introspectedTable)发现这个其实就是生成DOC的方法,因此,我们将 commentGenerator.addModelClassComment(topLevelClass, introspectedTable)注释掉,然后将里边的方法复制到我们SerializableAnnoPlugin里的makeSerializable()方法里,然后稍作修改:

 protected void makeSerializable(TopLevelClass topLevelClass,
            IntrospectedTable introspectedTable) {
        topLevelClass.addImportedType(this.defaultSerializer);
        topLevelClass.addImportedType(this.compatibleFieldSerializer);
        CommentGenerator commentGenerator = this.context.getCommentGenerator();
        topLevelClass.addJavaDocLine("/**");
        topLevelClass.addJavaDocLine(String.format("* %s设置Kryo的CompatibleFieldSerializer", introspectedTable.getRules().calculateAllFieldsClass().getShortName()));
        topLevelClass.addJavaDocLine("* 程序自动生成,无需更改。");
        topLevelClass.addJavaDocLine("*/");
        commentGenerator.addJavaFileComment(topLevelClass);
        StringBuilder sb = new StringBuilder();
        topLevelClass.addAnnotation(sb.append("@DefaultSerializer(").append(this.compatibleFieldSerializer.getShortName()).append(".class)").toString());
    }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

应用插件

最后一步,在Hello.xml加入我们的插件:

<plugin type="org.mybatis.generator.plugins.SerializableAnnoPlugin" />
<plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
<plugin type="org.mybatis.generator.plugins.EqualsHashCodePlugin" />
<plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
 
  • 1
  • 2
  • 3
  • 4

执行

执行一下吧,大功告成:
这里写图片描述


自定义分页插件

生成的Example动态查询中没有分页信息,默认生成的字段如下所示:
在这里插入图片描述
现在我们要得到这样的:
在这里插入图片描述
对应的sql xml文件中是这样的:
在这里插入图片描述

新建插件类PaginationPlugin

package org.mybatis.generator.plugins;

import org.mybatis.generator.api.CommentGenerator;
import org.mybatis.generator.api.IntrospectedTable;
import org.mybatis.generator.api.PluginAdapter;
import org.mybatis.generator.api.dom.java.*;
import org.mybatis.generator.api.dom.xml.Attribute;
import org.mybatis.generator.api.dom.xml.TextElement;
import org.mybatis.generator.api.dom.xml.XmlElement;

import java.util.List;

/**
 * Mysql分页插件
 *
 * @author zzs
 */
public class PaginationPlugin extends PluginAdapter {

    @Override
    public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
        addLimit(topLevelClass, introspectedTable, "limitStart");
        addLimit(topLevelClass, introspectedTable, "limitEnd");
        return super.modelExampleClassGenerated(topLevelClass, introspectedTable);
    }

    @Override
    public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        XmlElement isNotNullElement = new XmlElement("if");
        isNotNullElement.addAttribute(new Attribute("test", "limitStart &gt;= 0 and limitEnd &gt;= 0"));
        isNotNullElement.addElement(new TextElement("limit ${limitStart}, ${limitEnd}"));
        element.addElement(isNotNullElement);
        return super.sqlMapSelectByExampleWithoutBLOBsElementGenerated(element, introspectedTable);
    }

    @Override
    public boolean sqlMapSelectByExampleWithBLOBsElementGenerated(XmlElement element, IntrospectedTable introspectedTable) {
        XmlElement isNotNullElement = new XmlElement("if");
        isNotNullElement.addAttribute(new Attribute("test", "limitStart &gt;= 0 and limitEnd &gt;= 0"));
        isNotNullElement.addElement(new TextElement("limit ${limitStart}, ${limitEnd}"));
        element.addElement(isNotNullElement);
        return super.sqlMapSelectByExampleWithBLOBsElementGenerated(element, introspectedTable);
    }

    private void addLimit(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String name) {
        CommentGenerator commentGenerator = this.context.getCommentGenerator();
        Field field = new Field();
        field.setVisibility(JavaVisibility.PROTECTED);
        field.setType(FullyQualifiedJavaType.getIntInstance());
        field.setName(name);
        field.setInitializationString("-1");
        commentGenerator.addFieldComment(field, introspectedTable);
        topLevelClass.addField(field);
        char c = name.charAt(0);
        String camel = Character.toUpperCase(c) + name.substring(1);
        Method method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setName("set" + camel);
        method.addParameter(new Parameter(FullyQualifiedJavaType.getIntInstance(), name));
        method.addBodyLine("this." + name + "=" + name + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
        method = new Method();
        method.setVisibility(JavaVisibility.PUBLIC);
        method.setReturnType(FullyQualifiedJavaType.getIntInstance());
        method.setName("get" + camel);
        method.addBodyLine("return " + name + ";");
        commentGenerator.addGeneralMethodComment(method, introspectedTable);
        topLevelClass.addMethod(method);
    }

    @Override
    public boolean validate(List<String> warnings) {
        return true;
    }
}

使用

<plugin type="org.mybatis.generator.plugins.PaginationPlugin"/>
MyBatis Generator 插件 使用ORM框架必须保持数据表、POJO实体类、 mapper接口方法和映射文件四者一致,如果数据表发生变化,则需要同时修改另外三个文件,过程非常烦琐,易出错。MyBatis Generator插件是MyBatis提供的自动插件,只需几步就能生成POJO实体类、mapper映射文件和接口文件。Spring Boot支持MyBatis Generator自动生成代码插件。以下的内容演示MyBatis Generator插件的使用。 阅读详情

相关推荐

5.6 Mybatis代码生成器Mybatis Generator (MBG)实战详解

本文我们主要实战Mybatis官方的代码生成器:Mybatis Generator(MBG),掌握它以后,可以简化大部分手写代码,我们只需要写复杂逻辑代码!创建与MySQL表对应的Java PO对象,字段一一对应;创建Mapper接口,和对应的XML映射文件(或注解)。首先是PO对象,我们往往创建与数据库表字段一一对应的PO对象;其次在Mapper里,通常都会写基本的增删改查代码。本文就带大家实战一下Mybatis Generator,用它生成实战项目全部表的PO对象和基本增删改查代码!

天罡gg的专栏 3万+

Mybatis代码生成器Mybatis-Generator使用详解

前提 最近在做创业项目的时候因为有比较多的新需求,需要频繁基于DDL生成Mybatis适合的实体、Mapper接口和映射文件。其中,代码生成器是MyBatis Generator(MBG),用到了Mybatis-Generator-Core相关依赖,这里通过一篇文章详细地分析这个代码生成器的使用方式。本文编写的时候使用的Mybatis-Generator版本为1.4.0,其他版本没有进行过调研。 引入插件 Mybatis-Generator的运行方式有很多种: 基于mybatis-generator

m0_67698950的博客 4万+

Latex | IEEE双栏排版中长公式的排版

IEEE双栏排版中,有时候公式过于长了,过宽,导致单栏排版不下。

qq_43466146的博客 1万+

MyBatis入门(十)选修之MyBatis Generator

目录一.工具引入二.增删改方法三.查询方法四.复杂查询1.多条件and查询2.多条件or查询 MyBatis生成器介绍 MyBatis Generator (MBG)是MyBatis MyBatis的代码生成器。它将为所有版本的MyBatis生成代码。它将内省一个数据库表(或多个表)并生成可用于访问表的工件。这减少了设置对象和配置文件以与数据库表交互的初始麻烦。MBG试图对大量简单的CRUD (创建、检索、更新、删除)的数据库操作产生重大影响。您仍然需要为连接查询或存储过程编写SQL和对象代码。1. 准备数

长夜漫漫,无心睡眠 6605

基于迁移学习的花卉识别系统

本研究采用了预训练的经典深度学习模型,包括ResNet、AlexNet和VGG,通过微调的方式在Oxford-102数据集和一个自制花卉数据集上进行训练,并对图像进行了多种数据增强操作,以提高模型的泛化能力,致力于实现花卉的高效分类。

m0_71146337的博客 2375

告别PS串口焦虑:深入解析AXI Uartlite IP核如何将ZYNQ PL任意引脚变成串口(从Vivado约束到驱动文件全解析)

本文深入解析AXI Uartlite IP核如何将ZYNQ PL任意引脚变成串口,从Vivado约束到驱动文件全解析。通过AXI Uartlite IP核,开发者可以突破PS端UART数量限制,实现高效串口通信。文章详细介绍了IP核架构、Vivado工程配置、驱动开发及调试技巧,帮助开发者快速掌握这一技术。

weixin_33736649的博客 464

MyBatis逆向工具(Mybatis-Generator )【简单易懂!】

关于MyBatis逆向工具的学习一、配置逆向工具二、自动生成代码三、如何进行查询1、Generator提供的简单查询2、如何使用Generator进行复杂查询【重点内容】1、如何进行复杂查询2、代码分析【源码分析】3、基本CRUD实现【前面不懂不要紧,看了这个你就很清晰了,再去看前面的就明白了这前后的关系了】4、and,or复合查询5、模糊查询四、关于MyBatis-Generator需要注意的地方 一、配置逆向工具 配置插件信息【mybatis-generator 逆向工具插件,固定写法】 <p

weixin_44723016的博客 2358

RDMA网络通信技术、NCCL集合通讯(GPU)

NCCL 是 NVIDIA 专为 GPU 设计的高性能集合通信库,用于优化多 GPU、多节点间的集体操作(如 AllReduce、Broadcast、AllGather 等),是深度学习分布式训练的核心组件。RDMA 允许计算机直接访问远程内存,无需 CPU 干预,大幅降低了网络通信的延迟和 CPU 开销。常见的 RDMA 实现包括。是两项关键技术,用于优化节点间数据传输效率和大规模并行计算性能。在高性能计算(HPC)、人工智能训练和数据密集型场景中,

weixin_42795092的博客 2421

【springboot源码分析】- springboot项目搭建

文章目录1 快速搭建一个springboot + mybatis的项目1.1 新建一个springboot项目1.2 创建一个sb2的数据库,并且新建一个user表1.3 mybatis generate插件自动生成实体类和Mappers1.4 实现根据主键id查询用户信息的功能 本博客源码地址 https://github.com/suchahaerkang/spring-boot-study...

suchahaerkang的博客 326

使用在UE5中使用AirSim插件Eigen库头文件引用报错,出现报错的解决方式

如图所示,用红线圈出的两条头文件引用会报错,提示无法找到他们,但是可以发现的是,他们的路径书写是没有问题的。在终端中运行时候,会出现如下的报错。

GFCLJY的博客 1006

手把手教你用Cadence Virtuoso仿真一个5GHz CMOS分频器:从原理图到后仿,避开那些教科书上没写的坑

本文详细介绍了在Cadence Virtuoso环境中设计5GHz CMOS分频器的完整流程,从原理图设计到后仿真优化,特别关注实际工程中容易被忽视的关键问题。内容涵盖分频器核心指标解析、锁存器拓扑选择、接口设计匹配以及后仿真优化策略,为射频集成电路设计提供实用避坑指南。

weixin_26934905的博客 262

angularjs增删改查数据_老司机教你如何快速整合Mybatis,根据数据库自动生成增删改查...

最早接触Mybatis是在2016年,那时候学习只会用,根本不知道什么原理,不懂的原理的地方只能死记。搭建一个小的demo放到github,工作需要的时候就直接复制黏贴,虽然也能满足工作需要但是总感觉到不带劲。在找到工作后大概2017年时候就对Mybatis的源码进行了阅读,其实相对于Dubbo,Spring,之类的源码Mybatis的源码还是非常简单的,现在也经常推荐给刚开始学习看源码的同学。很...

weixin_39855843的博客 86

1.用Python写了一个进销存管理的软件~需求分析&界面设计&数据库设计&技术路线选择~

前端也打算用Python来写,查了很多的python库,大家比较推荐的是wxpython和QPython,之前接触过一点点的QT,确实非常强大,但是库比较重量级,自己写的这个东西明显配不上QT,又详细调研了一下wxpython,就是它了!

pepper_lihui的专栏 4321

MyBatis-Plus:高性能数据访问层增强框架架构设计与CRUD效率提升80%的解决方案

MyBatis-Plus作为MyBatis的增强工具库,通过创新的架构设计解决了传统ORM框架在CRUD操作中的重复代码问题,实现了数据访问层开发效率的显著提升。该框架采用零侵入设计理念,完全兼容原生MyBatis,同时提供了自动化代码生成、智能条件构造、多数据库支持等核心特性,特别适合企业级应用开发中的复杂数据访问场景。在微服务架构和分布式系统中,MyBatis-Plus通过Lambda表达式构

gitblog_00207的博客 220

STM32G0/G4电机控制中三角函数与浮点运算优化实战

三角函数(sin/cos)和浮点运算是电机矢量控制(FOC)的核心基础,直接影响Clark/Park变换、SVPWM及观测器的实时性与精度。在资源受限的MCU上,标准math.h库因无硬件FPU支持而引发严重时序抖动,导致电流环延迟、转矩脉动增大甚至系统复位。通过硬件加速(如STM32G4的CORDIC协处理器)或定制化定点算法(如STM32G0的查表+线性插值),可将sin/cos计算从数十微秒压缩至1–3 μs量级,提升30倍以上效率。该方案广泛适用于BLDC/PMSM驱动、伺服控制及高动态响应嵌入式运

weixin_35751412的博客 107

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

SpringBoot整合MyBatis-Plus实战与优化指南

MyBatis-Plus作为MyBatis的增强工具,通过自动化CRUD操作和智能SQL生成,显著提升了Java开发效率。其核心原理是基于MyBatis的拦截器机制,动态生成SQL语句,同时提供Lambda表达式构建类型安全的查询条件。在技术价值上,不仅减少了60%以上的样板代码,还通过批量操作优化、逻辑删除等特性提升了系统性能。典型应用场景包括后台管理系统、企业级应用开发等,特别是在需要快速迭代的业务系统中优势明显。结合SpringBoot的自动配置特性,开发者可以快速搭建高可用的数据访问层,同时通过分页

weixin_33698043的博客 463

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

【MyBatis】学习纪要十一:逆向工程

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34192993的博客 99

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

SpringBoot系列-- SpringBoot之Mybatis Generator生成器使用(多种方式)

目录1. 项目环境2. 引入依赖3.生成基于.xml形式使用mybatis的各层文件3.1 用generatorConfig.xml配置插件生成3.1.1 编写generatorConfig.xml文件3.1.2 关于generatorConfig.xml配置文件的补充说明3.1.3 开始生成文件3.2 使用Idea插件生成3.2.1 better-mybatis-generator插件简介3.2.2 better-mybatis-generator使用3.2.3 功能更强大的MyBatisCodeHelp

Gang-bb的博客 1万+

Mybatis代码生成器Mybatis-Generator使用及配置详解

XML配置文件才是Mybatis-Generator的核心,它用于控制代码生成的所有行为。所有非标签独有的公共配置的Key可以在mybatis-generator-core的PropertyRegistry类中找到。点击 IDEA 右侧 Maven,在 Plugins 下的 mybatis-generator 下,双击 mybatis-generator:generate 运行。4.执行MyBatis-Generator。

LM_Hh的博客 2万+

Mybatis generator配置(超详细)

超详细Mybatis Generator配置解析,本人学习Mybatis Generator官网时的学习笔记

Simon的博客 7573

MyBatisGenerator数据层代码生成器使用教程

MyBatis Generator数据层代码生成MyBatis Generator简介MBG的使用流程1、pom.xml中添加依赖 MyBatis Generator简介 MyBatis Generator(MBG)是一个代码生成器,能够基于数据库表(一或多张表)生成用于访问表的构件,例如:实体类、mapper接口、mapper.xml等。 MBG的使用流程 1、pom.xml中添加依赖 <!-- MyBatis 生成器 --> <dependency> <g

liubaojun的博客 2127

17代码生成器插件MyBatis Generator

Mybatis Generator简称 MBG,是一个专门为 MyBatis和 ibatis框架使用者提供的代码生成器。也可以快速的根据数据表生成对应的pojo类、Mapper接口、Mapper文件,甚至生成QBC风格的查询对象。一般在项目中,根据MyBatis Generator的XML配置文件设置生成简单的CRUD,但是复杂的查询或者有关关联的操作还是需要我们写SQL完成。pom.xml引出 mybatis-generator 坐标。

本人主攻Java,希望自己能成为出色的程序员。 2740

mybatis generator(保姆级教学)

mybatis generator,xml逆向生成工具

an_gentle_killer的博客 8916
上一篇: commons-fileUpload实现文件上传下载
下一篇: axios+springboot实现文件上传(文件以及表单)、下载(post/get方式)
淋雨一直走啊
博客等级 码龄11年 39粉丝 46原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值