MyBatis Generator實現MySQL分頁插件

MyBatis Generator是一個很是方便的代碼生成工具,它可以根據表結構生成CRUD代碼,能夠知足大部分需求。可是惟一讓人不爽的是,生成的代碼中的數據庫查詢沒有分頁功能。本文介紹如何讓MyBatis Generator生成的代碼具備分頁功能。html

MyBatis Generator結合Maven的配置和使用


在實現分頁以前,首先簡單介紹MyBatis Generator如何使用。java

MyBatis Generator配置文件

MyBatis Generator一般會有一個xml配置文件,用來指定鏈接的數據庫、哪些表、如何生成代碼。詳情能夠參考官方文檔:http://www.mybatis.org/generator/configreference/xmlconfig.html 。下面給出一份簡單的配置,
文件命名爲generatorConfig.xml:mysql

<?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 。git

下面給出一份簡單的pom.xml的配置:github

<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命令來生成代碼。固然,若是隻有上面的這些配置,生成的代碼是不支持分頁的。sql

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),能夠在代碼中調用這個方法來實現分頁:api

int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);
List<Xxx> list = xxxMapper.selectByExampleWithRowbounds(example, 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來實現分頁。mybatis

limit分頁插件實現


在實現MySQL分頁時更推薦使用select語句的limit來實現分頁,然而MyBatis Generator目前並無提供這樣的插件。好在MyBatis Generator支持插件擴展,咱們能夠本身實現一個基於limit來分頁的插件。如何實現一個插件能夠參考官方文檔:http://www.mybatis.org/generator/reference/pluggingIn.html 。app

實現思路
在生成的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

相關文章
相關標籤/搜索