转:mybatis - 分页功能
本文轉自http://www.cnblogs.com/jcli/archive/2011/08/09/2132222.html
mybatis高級應用系列一:分頁功能
Mybatis3.0出來已有段時間了,其實自己挺喜歡這樣的一個持久化框架的,因為它簡單實用,學習成本低。Mybatis3.0在整體結構上和ibatis2.X差不多,改進特性如下:
1.?????????解析xml引進了Xpath,不像ibatis2.x那樣業余
2.?????????動態sql用OGNL解析
3.?????????加入注解配置sql,感覺沒什么特別大的用途,我更喜歡xml方式,代碼和配置分離,這也是ibatis的初衷
4.?????????加強了緩存這塊的功能。Mybatis3.0把緩存模塊分得更細,分為“持久實現(prepetual)”和“資源回收策略實現(eviction)”,更好的對緩存功能進行自己組合和擴展
5.?????????終于加入的plugin功能,就像struts一樣,這樣就可以很好的擴展內部的Executor,,StatementHandler….等內部對象功能。
?
一下只能想到這些了,總之改動后的代碼結構清晰多了,如果各位看下源碼的話,也是學習設計模式很好的課件,里面的代碼用到了很多經典的設計模式,這在之后的系列學習中會講到。
?
這一篇文章講下分頁的功能。
?
正如和ibatis以前的版本一樣,mybatis的分頁還是基于內存分頁(查找出所有記錄再取出偏移量的記錄,如果jdbc驅支持absolute定位或者rs.next()到指定偏移位置),其實這樣的分頁實現基本沒用,特別是大量數據情況下。
?
要想改變mybatis內部的分頁行為,理論上只要把最終要執行的sql轉變成對應的分頁語句就行了。首先,我們熟悉下mybatis內部執行查詢的動態交互圖:
?可以很清楚的看到,真正生成Statement并執行sql的語句是StatementHandler接口的某個實現,這樣就可以寫個插件對StatementHandler的行為進行攔截。
package study.mybatis.interceptor;import java.sql.Connection;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import study.mybatis.dialect.Dialect;
import study.mybatis.dialect.MySql5Dialect;
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
publicclass PaginationInterceptor implements Interceptor{
privatefinalstatic Log log = LogFactory.getLog(PaginationInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
BoundSql boundSql = statementHandler.getBoundSql();
MetaObject metaStatementHandler = MetaObject.forObject(statementHandler);
RowBounds rowBounds = (RowBounds)metaStatementHandler.getValue("delegate.rowBounds");
if(rowBounds ==null|| rowBounds == RowBounds.DEFAULT){
return invocation.proceed();
}
Configuration configuration = (Configuration)metaStatementHandler.getValue("delegate.configuration");
Dialect.Type databaseType =null;
try{
databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
} catch(Exception e){
//ignore
}
if(databaseType ==null){
thrownew RuntimeException("the value of the dialect property in configuration.xml is not defined : "+ configuration.getVariables().getProperty("dialect"));
}
Dialect dialect =null;
switch(databaseType){
case MYSQL:
dialect =new MySql5Dialect();
}
String originalSql = (String)metaStatementHandler.getValue("delegate.boundSql.sql");
metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()) );
metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET );
metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT );
if(log.isDebugEnabled()){
log.debug("生成分頁SQL : "+ boundSql.getSql());
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
publicvoid setProperties(Properties properties) {
}
}
?
里面最重要的三條語句:
metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()) );metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET );
metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT ); 改別要執行的sql語句,現在新設置的sql語句是物理分頁的,所以現在不再需要mybatis進行額外的操作了,所以把rowBounds的偏移量恢復為初始值(offet:0,limit:Integer.max) 為了指定數據庫版本,在mybatis全局配置文件設置dialect值 <properties>
<property name="dialect" value="mysql"/>
</properties>
<plugins>
<plugin interceptor="study.mybatis.interceptor.PaginationInterceptor">
</plugin>
</plugins>
完整代碼請用svn從下面鏈接檢出查看:
svn checkout?http://20110311start.googlecode.com/svn/trunk/
下個系列將會講下緩存的擴展應用。 -----------------------------分隔線---------------------------------------------最近有朋友用mybatis和spring整合的時候如果按照下列方式發現dialect屬性不能設置成功:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="typeAliasesPackage" value="com.***.web.domain" /><property name="plugins"><array><ref bean="paginationInterceptor"/></array></property><property name="configurationProperties"><props><prop key="dialect">mysql</prop></props></property> </bean>?
這個問題是org.mybatis.spring.SqlSessionFactoryBean這個代碼里有個bug(244行,或者不是bug,是作者不想這么做法),如果感興趣可以看下源碼。配置文件做下如下修改:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="typeAliasesPackage" value="com.***.web.domain" /><property name="plugins"><array><ref bean="paginationInterceptor"/></array></property><!-- 這里不要,注釋掉<property name="configurationProperties"><props><prop key="dialect">mysql</prop></props></property>--><!-- 加上這個屬性 --><property name="configLocation" value="classpath:Mybatis_Configuration.xml"/> </bean>?
Mybatis_Configuration.xml的配置如下:
1 <?xml version="1.0" encoding="UTF-8" ?>2 <!DOCTYPE configuration
3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
4 "http://mybatis.org/dtd/mybatis-3-config.dtd">
5 <configuration>
6
7 <properties>
8 <property name="dialect" value="oracle"/>
9 </properties>
10
11 </configuration>
?
原創文章,轉載請注明出處,謝謝!
分類:?java 標簽:?mybatis分頁 mybatis 綠色通道:?好文要頂?關注我?收藏該文與我聯系? 海鳥關注 - 2
粉絲 - 7 +加關注 0 0 (請您對文章做出評價) ??博主上一篇:DriverManager怎樣查找當前Driver
??博主下一篇:java中顯示設置實例為null多余嗎 posted @?2011-08-09 14:07?海鳥?閱讀(8189) 評論(6)?編輯?收藏
評論列表 回復引用 #1樓?2011-09-07 09:30?kugua-? delegate.rowBounds.offset 是在哪定義的? 為什么要設置這個屬性呢? 支持(0)反對(0) 回復引用 #2樓[樓主]?2011-09-07 11:21?海鳥? @kugua-
我們調用分頁的API一般是這樣的:
?
| 1 | List l = session.selectList("users.selectUsers", user,new?RowBounds(2, 1)); |
mybatis默認情況下全部查詢出來后再截取當前頁的記錄。可是如果我們自己寫了個分頁插件的情況下采用了物理分頁,查詢出來的記錄就是當前頁的內容。所以現在要告訴mybatis不再對結果進行加工,所以要重置為不分頁的設置:
?
| 1 2 3 | metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET ); metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT ); |
這兩條語句就是用反射機制設置,相當于:
?
| 1 2 3 | delegate.getRowBounds.setLimit(RowBounds.NO_ROW_LIMIT); delegate.getRowBounds.setOffset(RowBounds.NO_ROW_OFFSET); |
轉載于:https://www.cnblogs.com/samkin/articles/2864593.html
總結
以上是生活随笔為你收集整理的转:mybatis - 分页功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用DOS命令找到占用80端口的程序,关
- 下一篇: 正确处理 Azure OnStop 事件