MyBatis Generator是一個很是方便的代碼生成工具,它可以根據表結構生成CRUD代碼,能夠知足大部分需求。可是惟一讓人不爽的是,生成的代碼中的數據庫查詢沒有分頁功能。本文介紹如何讓MyBatis Generator生成的代碼具備分頁功能。html
在實現分頁以前,首先簡單介紹MyBatis Generator如何使用。java
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&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>
官網文檔中提供了四種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
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
在實現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
。