设为首页 收藏本站
查看: 1165|回复: 0

[经验分享] mybatis高级应用系列一:拦截器实现分页功能

[复制链接]

尚未签到

发表于 2016-11-27 07:42:13 | 显示全部楼层 |阅读模式








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内部执行查询的动态交互图:

DSC0000.png





可以很清楚的看到,真正生成Statement并执行sql的语句是StatementHandler接口的某个实现,这样就可以写个插件对StatementHandler的行为进行拦截。  

DSC0001.gif

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 checkouthttp://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>

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-305929-1-1.html 上篇帖子: 个人对使用mybatis-generator的看法 下篇帖子: 分享一个spring+mybatis+velocity项目demo
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表