• MyBatis Generator实现MySQL分页插件


    MyBatis Generator是一个非常方便的代码生成工具,它能够根据表结构生成CRUD代码,可以满足大部分需求。但是唯一让人不爽的是,生成的代码中的数据库查询没有分页功能。本文介绍如何让MyBatis Generator生成的代码具有分页功能。

    MyBatis Generator结合Maven的配置和使用

    在实现分页之前,首先简单介绍MyBatis Generator如何使用。

    MyBatis Generator配置文件

    MyBatis Generator通常会有一个xml配置文件,用来指定连接的数据库、哪些表、如何生成代码。详情可以参考官方文档:http://www.mybatis.org/generator/configreference/xmlconfig.html 。下面给出一份简单的配置,
    文件命名为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>
        <context id="mysqlgenerator" targetRuntime="MyBatis3">
     
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://localhost:3306/yourdb?useUnicode=true&amp;characterEncoding=UTF-8"
                userId="user" password="password" />
     
            <javaModelGenerator targetPackage="com.xxg.bean" targetProject="src/main/java" />
            <sqlMapGenerator targetPackage="com.xxg.mapper" targetProject="src/main/resources" />
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.xxg.mapper" targetProject="src/main/java" />
     
            <table tableName="table_a" />
            <table tableName="table_b" />
            <table tableName="table_c" />
            <table tableName="table_d" />
     
        </context>
    </generatorConfiguration>
    

    Maven配置

    官网文档中提供了四种MyBatis Generator生成代码的运行方式:命令行、使用Ant、使用Maven、Java编码。本文采用Maven插件mybatis-generator-maven-plugin来运行MyBatis Generator,详细配置同样可以参考官方文档:http://www.mybatis.org/generator/running/runningWithMaven.html

    下面给出一份简单的pom.xml的配置:

    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.34</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    以上配置完成后,可以通过运行mvn mybatis-generator:generate命令来生成代码。当然,如果只有上面的这些配置,生成的代码是不支持分页的。

    RowBoundsPlugin

    MyBatis Generator可以通过插件机制来扩展其功能,其中RowBoundsPlugin是MyBatis Generator中自带的一个分页插件。可以在MyBatis Generator配置文件generatorConfig.xml中添加这个插件:

    <context id="mysqlgenerator" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin"></plugin>
        ...
    </context>
    

    再次运行mvn mybatis-generator:generate生成代码,此时会发现生成的Mapper中会加入一个新的方法:selectByExampleWithRowbounds(XxxExample example, RowBounds rowBounds),可以在代码中调用这个方法来实现分页:
    RowBounds的构造方法new RowBounds(offset, limit)中的offset、limit参数就相当于MySQL的select语句limit后的offset和rows。如果此时仔细观察一下日志打出来的SQL语句或者看下生成的XxxMapper.xml文件中的selectByExampleWithRowbounds元素,可以发现select语句并没有使用limit。实际上RowBounds原理是通过ResultSet的游标来实现分页,也就是并不是用select语句的limit分页而是用Java代码分页,查询语句的结果集会包含符合查询条件的所有数据,使用不慎会导致性能问题,所以并不推荐使用RowBoundsPlugin来实现分页。

    limit分页插件实现

    在实现MySQL分页时更推荐使用select语句的limit来实现分页,然而MyBatis Generator目前并没有提供这样的插件。好在MyBatis Generator支持插件扩展,我们可以自己实现一个基于limit来分页的插件。如何实现一个插件可以参考官方文档:http://www.mybatis.org/generator/reference/pluggingIn.html

    实现思路
    在生成的XxxExample中加入两个属性limit和offset,同时加上set和get方法。也就是需要生成以下代码:

    private Integer limit;
    private Integer offset;
    public void setLimit(Integer limit) {
        this.limit = limit;
    }
    public Integer getLimit() {
        return limit;
    }
    public void setOffset(Integer offset) {
        this.offset = offset;
    }
    public Integer getOffset() {
        return offset;
    }
    

    XxxMapper.xml中在通过selectByExample查询时,添加limit:

    <select id="selectByExample" parameterType="com.xxg.bean.XxxExample" resultMap="BaseResultMap">
      ...
      <if test="limit != null">
        <if test="offset != null">
          limit ${offset}, ${limit}
        </if>
        <if test="offset == null">
          limit ${limit}
        </if>
      </if>
    </select>
    

    插件实现代码

    package com.xxg.mybatis.plugins;
     
    import java.util.List;
     
    import org.mybatis.generator.api.IntrospectedTable;
    import org.mybatis.generator.api.PluginAdapter;
    import org.mybatis.generator.api.dom.java.Field;
    import org.mybatis.generator.api.dom.java.FullyQualifiedJavaType;
    import org.mybatis.generator.api.dom.java.JavaVisibility;
    import org.mybatis.generator.api.dom.java.Method;
    import org.mybatis.generator.api.dom.java.Parameter;
    import org.mybatis.generator.api.dom.java.PrimitiveTypeWrapper;
    import org.mybatis.generator.api.dom.java.TopLevelClass;
    import org.mybatis.generator.api.dom.xml.Attribute;
    import org.mybatis.generator.api.dom.xml.TextElement;
    import org.mybatis.generator.api.dom.xml.XmlElement;
     
    public class MySQLLimitPlugin extends PluginAdapter {
     
        @Override
        public boolean validate(List<String> list) {
            return true;
        }
     
        /**
         * 为每个Example类添加limit和offset属性已经set、get方法
         */
        @Override
        public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) {
     
            PrimitiveTypeWrapper integerWrapper = FullyQualifiedJavaType.getIntInstance().getPrimitiveTypeWrapper();
     
            Field limit = new Field();
            limit.setName("limit");
            limit.setVisibility(JavaVisibility.PRIVATE);
            limit.setType(integerWrapper);
            topLevelClass.addField(limit);
     
            Method setLimit = new Method();
            setLimit.setVisibility(JavaVisibility.PUBLIC);
            setLimit.setName("setLimit");
            setLimit.addParameter(new Parameter(integerWrapper, "limit"));
            setLimit.addBodyLine("this.limit = limit;");
            topLevelClass.addMethod(setLimit);
     
            Method getLimit = new Method();
            getLimit.setVisibility(JavaVisibility.PUBLIC);
            getLimit.setReturnType(integerWrapper);
            getLimit.setName("getLimit");
            getLimit.addBodyLine("return limit;");
            topLevelClass.addMethod(getLimit);
     
            Field offset = new Field();
            offset.setName("offset");
            offset.setVisibility(JavaVisibility.PRIVATE);
            offset.setType(integerWrapper);
            topLevelClass.addField(offset);
     
            Method setOffset = new Method();
            setOffset.setVisibility(JavaVisibility.PUBLIC);
            setOffset.setName("setOffset");
            setOffset.addParameter(new Parameter(integerWrapper, "offset"));
            setOffset.addBodyLine("this.offset = offset;");
            topLevelClass.addMethod(setOffset);
     
            Method getOffset = new Method();
            getOffset.setVisibility(JavaVisibility.PUBLIC);
            getOffset.setReturnType(integerWrapper);
            getOffset.setName("getOffset");
            getOffset.addBodyLine("return offset;");
            topLevelClass.addMethod(getOffset);
     
            return true;
        }
     
        /**
         * 为Mapper.xml的selectByExample添加limit
         */
        @Override
        public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated(XmlElement element,
                IntrospectedTable introspectedTable) {
     
            XmlElement ifLimitNotNullElement = new XmlElement("if");
            ifLimitNotNullElement.addAttribute(new Attribute("test", "limit != null"));
     
            XmlElement ifOffsetNotNullElement = new XmlElement("if");
            ifOffsetNotNullElement.addAttribute(new Attribute("test", "offset != null"));
            ifOffsetNotNullElement.addElement(new TextElement("limit ${offset}, ${limit}"));
            ifLimitNotNullElement.addElement(ifOffsetNotNullElement);
     
            XmlElement ifOffsetNullElement = new XmlElement("if");
            ifOffsetNullElement.addAttribute(new Attribute("test", "offset == null"));
            ifOffsetNullElement.addElement(new TextElement("limit ${limit}"));
            ifLimitNotNullElement.addElement(ifOffsetNullElement);
     
            element.addElement(ifLimitNotNullElement);
     
            return true;
        }
    }
    

    插件的使用

    在MyBatis Generator配置文件中配置plugin:

    <context id="mysqlgenerator" targetRuntime="MyBatis3">
        <plugin type="com.xxg.mybatis.plugins.MySQLLimitPlugin"></plugin>
        ...
    </context>
    

    如果直接加上以上配置运行mvn mybatis-generator:generate肯定会出现找不到这个插件的错误:

    java.lang.ClassNotFoundException: com.xxg.mybatis.plugins.MySQLLimitPlugin

    为了方便大家的使用,我已经把插件打包上传到GitHub,可以在pom.xml直接依赖使用:

    pluginRepositories>
        <pluginRepository>
            <id>mybatis-generator-limit-plugin-mvn-repo</id>
            <url>https://raw.github.com/wucao/mybatis-generator-limit-plugin/mvn-repo/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.34</version>
                    </dependency>
                    <dependency>
                        <groupId>com.xxg</groupId>
                        <artifactId>mybatis-generator-plugin</artifactId>
                        <version>1.0.0</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <overwrite>true</overwrite>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    此时运行mvn mybatis-generator:generate命令可以成功生成代码。

    使用生成的代码分页

    xxExample example = new XxxExample();
    ...
    example.setLimit(10); // page size limit
    example.setOffset(20); // offset
    List<Xxx> list = xxxMapper.selectByExample(example);
    

    以上代码运行时执行的SQL是:select ... limit 20, 10。

    XxxExample example = new XxxExample();
    ...
    example.setLimit(10); // limit
    List<Xxx> list = xxxMapper.selectByExample(example);
    

    以上代码运行时执行的SQL是:select ... limit 10。

  • 相关阅读:
    【数据库摘要】6_Sql_Inner_Join
    C# 实体类序列化与反序列化一 (XmlSerializer)
    MBProgressHUD 显示方向异常
    回溯算法
    Linux下Tomcat VM參数改动
    053第85题
    让你提前认识软件开发(26):数据库脚本的凝视
    可穿戴设备,或许无屏交互才是终极需求!
    Tomcat载入两次问题
    python字典构造函数dict(mapping)解析
  • 原文地址:https://www.cnblogs.com/zouwangblog/p/11139704.html
Copyright © 2020-2023  润新知