利用mybatis generator外掛生成基於分頁語句及分頁解決方案
1》
雖然MyBatis_Generator可以將常用的DAO中的方法都生成,但是唯獨忽視了一點——分頁,雖然MyBatis支援分頁,但是那個分頁是記憶體分頁,如果資料量大的話記憶體恐怕要承受不了,於是就自動動手改造自動化工具生成的程式碼使其支援真分頁.
本文裡面我說的是針對mysql的,針對其他資料庫的請做適當修改,因為mybatis底層沒有封裝資料庫的差異,所以我覺得也並沒有必要對其差異進行封裝.
其實改動的東西很簡單,也很少,切入點就是Mapper裡面的動態sql:
首先在Example物件裡面加入兩個整形變數start和limit,start代表起始索引,limit代表數量,然後生成對應的get/set方法,使MyBatis在執行的時候能夠獲取相應的值.
第二部就是修改Mapper了,在selectByExample裡面加入對Example物件中start和limit的判斷,如果start和limit不同時為0,則自動在sql的最後加入分頁條件,以下是需要加入的xml程式碼,記得加在selectByExample這個sql的最後面,也就是排序的後面
<if test="start !=0 or limit!=0"> limit #{start},#{limit} </if>2》
Mybatis分頁-利用Mybatis Generator外掛生成基於資料庫方言的分頁語句,統計記錄總數
眾所周知,Mybatis本身沒有提供基於資料庫方言的分頁功能,而是基於JDBC的遊標分頁,很容易出現效能問題。網上有很多分頁的解決方案,不外乎是基於Mybatis本機的外掛機制,通過攔截Sql做分頁。但是在像Oracle這樣的資料庫上,攔截器生成的Sql語句沒有變數繫結,而且每次語句的都要去攔截,感覺有點浪費效能。
Mybatis Generator是Mybatis的程式碼生成工具,可以生成大部分的查詢語句。
本文提供的分頁解決方案是新增Mybatis Generator外掛,在用Mybatis Generator生成Mybatis程式碼時,直接生成基於資料庫方言的Sql語句,解決Oralce等資料庫的變數繫結,且無需使用Mybatis攔截器去攔截語句判斷分頁。
一、編寫Mybatis Generator Dialect外掛
/**
* Copyright (C) 2011 Tgwoo Inc. * http://www.tgwoo.com/ */ package com.tgwoo.core.dao.plugin; import java.util.List; 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.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.TopLevelClass; import org.mybatis.generator.api.dom.xml.Attribute; import org.mybatis.generator.api.dom.xml.Document; import org.mybatis.generator.api.dom.xml.TextElement; import org.mybatis.generator.api.dom.xml.XmlElement; /** * @author pan.wei * @date 2011-11-30 下午08:36:11 */ public class OraclePaginationPlugin extends PluginAdapter { @Override public boolean modelExampleClassGenerated(TopLevelClass topLevelClass, IntrospectedTable introspectedTable) { // add field, getter, setter for limit clause addPage(topLevelClass, introspectedTable, "page"); return super.modelExampleClassGenerated(topLevelClass, introspectedTable); } @Override public boolean sqlMapDocumentGenerated(Document document, IntrospectedTable introspectedTable) { XmlElement parentElement = document.getRootElement(); // 產生分頁語句前半部分 XmlElement paginationPrefixElement = new XmlElement("sql"); paginationPrefixElement.addAttribute(new Attribute("id", "OracleDialectPrefix")); XmlElement pageStart = new XmlElement("if"); pageStart.addAttribute(new Attribute("test", "page != null")); pageStart.addElement(new TextElement( "select * from ( select row_.*, rownum rownum_ from ( ")); paginationPrefixElement.addElement(pageStart); parentElement.addElement(paginationPrefixElement); // 產生分頁語句後半部分 XmlElement paginationSuffixElement = new XmlElement("sql"); paginationSuffixElement.addAttribute(new Attribute("id", "OracleDialectSuffix")); XmlElement pageEnd = new XmlElement("if"); pageEnd.addAttribute(new Attribute("test", "page != null")); pageEnd.addElement(new TextElement( "<![CDATA[ ) row_ ) where rownum_ > #{page.begin} and rownum_ <= #{page.end} ]]>")); paginationSuffixElement.addElement(pageEnd); parentElement.addElement(paginationSuffixElement); return super.sqlMapDocumentGenerated(document, introspectedTable); } @Override public boolean sqlMapSelectByExampleWithoutBLOBsElementGenerated( XmlElement element, IntrospectedTable introspectedTable) { XmlElement pageStart = new XmlElement("include"); //$NON-NLS-1$ pageStart.addAttribute(new Attribute("refid", "OracleDialectPrefix")); element.getElements().add(0, pageStart); XmlElement isNotNullElement = new XmlElement("include"); //$NON-NLS-1$ isNotNullElement.addAttribute(new Attribute("refid", "OracleDialectSuffix")); element.getElements().add(isNotNullElement); return super.sqlMapUpdateByExampleWithoutBLOBsElementGenerated(element, introspectedTable); } /** * @param topLevelClass * @param introspectedTable * @param name */ private void addPage(TopLevelClass topLevelClass, IntrospectedTable introspectedTable, String name) { topLevelClass.addImportedType(new FullyQualifiedJavaType( "com.tgwoo.core.dao.pojo.Page")); CommentGenerator commentGenerator = context.getCommentGenerator(); Field field = new Field(); field.setVisibility(JavaVisibility.PROTECTED); field.setType(new FullyQualifiedJavaType("com.tgwoo.core.dao.pojo.Page")); field.setName(name); 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(new FullyQualifiedJavaType( "com.tgwoo.core.dao.pojo.Page"), name)); method.addBodyLine("this." + name + "=" + name + ";"); commentGenerator.addGeneralMethodComment(method, introspectedTable); topLevelClass.addMethod(method); method = new Method(); method.setVisibility(JavaVisibility.PUBLIC); method.setReturnType(new FullyQualifiedJavaType( "com.tgwoo.core.dao.pojo.Page")); method.setName("get" + camel); method.addBodyLine("return " + name + ";"); commentGenerator.addGeneralMethodComment(method, introspectedTable); topLevelClass.addMethod(method); } /** * This plugin is always valid - no properties are required */ public boolean validate(List<String> warnings) { return true; } }
二、增加外掛到Mybatis Generator配置檔案中
<?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="E:\work\eclipseWorkspace\myEclipse\CTSPMTS\WebRoot\WEB-INF\lib\ojdbc14.jar" /> <context id="oracle" > <plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin"></plugin> <plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin> <!-- Pagination --> <plugin type="com.tgwoo.core.dao.plugin.OraclePaginationPlugin"></plugin> <commentGenerator> <property name="suppressDate" value="true" /> <property name="suppressAllComments" value="true" /> </commentGenerator> <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver" connectionURL="jdbc:oracle:thin:@192.168.0.2:1521:ctspmt" userId="ctspmt" password="ctspmt123" /> <javaModelGenerator targetPackage="com.tgwoo.ctspmt.model" targetProject="CTSPMTS/src" /> <sqlMapGenerator targetPackage="com.tgwoo.ctspmt.data" targetProject="CTSPMTS/src" /> <javaClientGenerator targetPackage="com.tgwoo.ctspmt.data" targetProject="CTSPMTS/src" type="XMLMAPPER" /><!-- <table schema="ctspmt" tableName="mt_e_interface_log"/> --><!-- <table schema="ctspmt" tableName="mt_e_msg" /> <table schema="ctspmt" tableName="mt_e_msg_log" /> <table schema="ctspmt" tableName="mt_e_msg_receiver" /> <table schema="ctspmt" tableName="st_e_org" /> <table schema="ctspmt" tableName="st_e_role" /> <table schema="ctspmt" tableName="st_e_user" /> <table schema="ctspmt" tableName="mt_e_user_msg_conf" /> <table schema="ctspmt" tableName="mt_j_user_device" /> <table schema="ctspmt" tableName="st_j_user_role" /> <table schema="ctspmt" tableName="ST_E_UNIQUE_KEY" /> --><table schema="ctspmt" tableName="mt_v_msg_item" /> </context> </generatorConfiguration>
三、示例
/**
* Copyright (C) 2011 Tgwoo Inc.
* http://www.tgwoo.com/
*/
package com.tgwoo.ctspmt.test;
import com.tgwoo.core.config.SpringBeanProxy;
import com.tgwoo.core.dao.pojo.Page;
import com.tgwoo.ctspmt.data.MtVMsgItemMapper;
import com.tgwoo.ctspmt.model.MtVMsgItemExample;
/**
* @author pan.wei
* @date 2011-11-25 下午01:26:17
*/
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
//get spring mapper instance
MtVMsgItemMapper mapper = SpringBeanProxy.getCtx().getBean(
MtVMsgItemMapper.class);
MtVMsgItemExample ex = new MtVMsgItemExample();
Page page = new Page(0, 10);
ex.setPage(page);
ex.createCriteria().andMsgCodeEqualTo("222");
// set count,up to you
page.setCount(mapper.countByExample(ex));
int row = mapper.selectByExample(ex).size();
System.out.println("============row:" + row + "================");
}
}
四、分頁類
package com.tgwoo.core.dao.pojo;
/**
* @author pan.wei
* @date 2011-12-1 上午11:36:12
*/
public class Page {
// 分頁查詢開始記錄位置
private int begin;
// 分頁檢視下結束位置
private int end;
// 每頁顯示記錄數
private int length;
// 查詢結果總記錄數
private int count;
// 當前頁碼
private int current;
// 總共頁數
private int total;
public Page() {
}
/**
* 建構函式
*
* @param begin
* @param length
*/
public Page(int begin, int length) {
this.begin = begin;
this.length = length;
this.end = this.begin + this.length;
this.current = (int) Math.floor((this.begin * 1.0d) / this.length) + 1;
}
/**
* @param begin
* @param length
* @param count
*/
public Page(int begin, int length, int count) {
this(begin, length);
this.count = count;
}
/**
* @return the begin
*/
public int getBegin() {
return begin;
}
/**
* @return the end
*/
public int getEnd() {
return end;
}
/**
* @param end
* the end to set
*/
public void setEnd(int end) {
this.end = end;
}
/**
* @param begin
* the begin to set
*/
public void setBegin(int begin) {
this.begin = begin;
if (this.length != 0) {
this.current = (int) Math.floor((this.begin * 1.0d) / this.length) + 1;
}
}
/**
* @return the length
*/
public int getLength() {
return length;
}
/**
* @param length
* the length to set
*/
public void setLength(int length) {
this.length = length;
if (this.begin != 0) {
this.current = (int) Math.floor((this.begin * 1.0d) / this.length) + 1;
}
}
/**
* @return the count
*/
public int getCount() {
return count;
}
/**
* @param count
* the count to set
*/
public void setCount(int count) {
this.count = count;
this.total = (int) Math.floor((this.count * 1.0d) / this.length);
if (this.count % this.length != 0) {
this.total++;
}
}
/**
* @return the current
*/
public int getCurrent() {
return current;
}
/**
* @param current
* the current to set
*/
public void setCurrent(int current) {
this.current = current;
}
/**
* @return the total
*/
public int getTotal() {
if (total == 0) {
return 1;
}
return total;
}
/**
* @param total
* the total to set
*/
public void setTotal(int total) {
this.total = total;
}
}
五、生成後的程式碼
1、Example程式碼
package com.tgwoo.ctspmt.model;
import com.tgwoo.core.dao.pojo.Page;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
public class MtVMsgItemExample {
protected String orderByClause;
protected boolean distinct;
protected List<Criteria> oredCriteria;
protected Page page;
...
2、mapper.xml
... <select id="selectByExample" resultMap="BaseResultMap" parameterType="com.tgwoo.ctspmt.model.MtVMsgItemExample" > <include refid="OracleDialectPrefix" /> select <if test="distinct" > distinct </if> <include refid="Base_Column_List" /> from CTSPMT.MT_V_MSG_ITEM <if test="_parameter != null" > <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null" > order by ${orderByClause} </if> <include refid="OracleDialectSuffix" /> </select> ... <sql id="OracleDialectPrefix" > <if test="page != null" > select * from ( select row_.*, rownum rownum_ from ( </if> </sql> <sql id="OracleDialectSuffix" > <if test="page != null" > <![CDATA[ ) row_ ) where rownum_ > #{page.begin} and rownum_ <= #{page.end} ]]> </if> </sql> ...
附件是Mybatis Generatorjar包。
其他資料庫的方言可以按照Oracle的去改寫,有寫好的希望能共享下。
-------------------------------------------------------------------
maven管理:
1、pom.xml
<build> <plugins> <plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.3.1</version> <executions> <execution> <id>Generate MyBatis Artifacts</id> <goals> <goal>generate</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc14</artifactId> <version>10.2.0.4.0</version> </dependency> </dependencies> </plugin> </plugins> </build>
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> <context id="oracleGenerator" targetRuntime="MyBatis3"> <plugin type="org.mybatis.generator.plugins.CaseInsensitiveLikePlugin"></plugin> <plugin type="org.mybatis.generator.plugins.SerializablePlugin"></plugin> <!-- Pagination --> <plugin type="com.tgwoo.test.core.dao.mybatis.generator.plugin.pagination.OraclePaginationPlugin"></plugin> <!-- 取消註釋 --> <commentGenerator> <property name="suppressDate" value="true" /> <property name="suppressAllComments" value="true" /> </commentGenerator> <!-- 配置連線資料資訊 --> <jdbcConnection driverClass="oracle.jdbc.driver.OracleDriver" connectionURL="jdbc:oracle:thin:@192.168.0.2:1521:test" userId="test" password="test123" /> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- 配置自動生成的Model的儲存路徑與其它引數 --> <javaModelGenerator targetPackage="com.tgwoo.test.dao.model" targetProject=".\src\main\java"> <property name="enableSubPackages" value="false" /> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- 配置自動生成的Mappper.xml對映的儲存路徑與其它引數 --> <sqlMapGenerator targetPackage="com.tgwoo.test.dao" targetProject=".\src\main\resources"> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- 配置自動生成的Mappper.java介面的儲存路徑與其它引數 --> <javaClientGenerator type="XMLMAPPER" targetPackage="com.tgwoo.test.dao" targetProject=".\src\main\java"> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 生成表對應的操作與實體物件 --> <table schema="test" tableName="testTable"> <columnOverride column="id" javaType="Long" /> </table> </context> </generatorConfiguration>
3、run
Goals:mybatis-generator:generate
4、注意事項
報外掛無法找到或者無法例項化的一般是分頁外掛和maven外掛不在同一classloader下引起的,需要在mybatis-generator-maven-plugin的dependencies中增加dependency。
1 樓 hellostory 2012-02-07 如果是做【高階查詢】分頁顯示,你這個如何實現? 2 樓 daquan198163 2012-02-08 Good,太有用了! 3 樓 chengfengmuyu 2012-02-19 4 樓 hgdakfg 2012-03-21 hellostory 寫道 如果是做【高階查詢】分頁顯示,你這個如何實現?寫檢視. 5 樓 panqili2120 2012-05-28 很強大 支援 呵呵,但在我們用的Page一般的begin是從1開始的, 也就是說上面的SQL的這部分也改成row_ ) where rownum_ >= #{page.begin}這樣 6 樓 duohuoteng 2012-07-06