`
ly_ltw
  • 浏览: 9531 次
文章分类
社区版块
存档分类
最新评论

mybatis高级应用系列一:拦截器实现分页功能

 
阅读更多


Mybatis3.0出来已有段时间了,其实自己挺喜欢这样的一个持久化框架的,因为它简单实用,学习成本低。Mybatis3.0在整体结构上和ibatis2.X差不多,改进特性如下:

1.解析xml引进了Xpath,不像ibatis2.x那样业余

2.动态sqlOGNL解析

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 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>

分享到:
评论

相关推荐

    mybatis使用拦截器实现分页操作

    在本案例中,我们将探讨如何利用MyBatis的拦截器功能实现分页操作,以此来提高代码的复用性和可维护性。下面将详细阐述这一过程。 首先,我们需要了解MyBatis拦截器的基本概念。MyBatis拦截器类似于Java的AOP(面向...

    Mybatis分页拦截器

    Mybatis分页拦截器是Mybatis框架中一种实用的扩展机制,用于实现高效的数据库查询分页功能。在Mybatis中,拦截器扮演着插件的角色,可以监听并修改Mybatis执行过程中的某些行为。本话题将深入探讨Mybatis分页拦截器...

    springboot+mybatis拦截器实现自动分页

    本教程将详细讲解如何利用拦截器来实现这一功能。 首先,我们需要理解Spring Boot和MyBatis的基础。Spring Boot简化了Spring应用程序的开发过程,它提供了预配置的依赖项、内嵌的HTTP服务器(如Tomcat)以及自动...

    MyBatis拦截器分页与动态修改SQL及其参数值

    在"MyBatis拦截器分页与动态修改SQL及其参数值"的主题中,我们可以深入理解以下几个关键知识点: 1. **MyBatis拦截器**:MyBatis提供了一种插件机制,即拦截器(Interceptor),它基于Java的动态代理,可以在SQL...

    mybatis 分页拦截器及拦截器配置

    MyBatis 分页拦截器是实现数据库查询分页效果的一种高效解决方案。在传统的SQL查询中,我们通常需要手动编写分页语句,但这容易出错且不易维护。通过使用分页拦截器,我们可以将分页逻辑封装起来,使得在编写Mapper...

    mybatis分页拦截器(自动封装版)剖析.pdf

    MyBatis 分页拦截器是一种优化数据库操作的技术,它的主要目的是在不修改大量现有业务代码的情况下,实现对数据库查询结果的自动分页。在自动封装版的MyBatis分页拦截器中,开发者通常会创建一个拦截器类,该类会...

    MyBatis拦截器分页

    MyBatis拦截器分页是实现数据库查询优化和提高应用性能的一种有效手段。在MyBatis框架中,拦截器扮演着动态代理的角色,允许我们在执行SQL之前或之后进行额外的操作,比如统计、日志记录或者在本例中的分页处理。...

    MyBatis拦截器实现分页功能的实现方法

    总结来说,MyBatis拦截器实现分页功能需要定义一个分页对象来存储分页参数,然后通过实现Interceptor接口并重写intercept方法来修改原始SQL语句,最后在MyBatis配置中正确注册拦截器。这样就可以在不侵入原有业务...

    MyBatis拦截器及分页插件

    MyBatis拦截器是MyBatis框架中一个强大的功能,它允许用户在执行某些方法之前或之后执行某些逻辑。拦截器的使用可以极大地扩展MyBatis的功能,使其更加灵活和强大。 拦截器的设计初衷是为了供用户在某些时候可以...

    spring+springMVC+mybatis拦截器分页 源码

    综上所述,"spring+springMVC+mybatis拦截器分页"项目结合了三大框架的优势,通过SpringMVC的拦截器实现业务逻辑的扩展,利用MyBatis的分页插件处理后台数据,再由EasyUI提供友好的用户界面。这样的组合为高效且可控...

    Mybatis拦截器介绍及分页插件示例

    本文将详细介绍Mybatis拦截器的原理及其应用,并通过一个具体的分页插件示例来展示如何使用拦截器。 #### 1.3 Interceptor接口 Mybatis提供了`Interceptor`接口作为自定义拦截器的基础。此接口包含三个主要方法: ...

    mybatis分页拦截器

    MyBatis拦截器是基于AOP(面向切面编程)的一种机制,它允许我们在特定的执行点(如SQL语句的执行、参数的设置等)插入自定义的行为。分页拦截器就是在执行SQL之前或之后,对SQL语句进行修改,添加必要的分页条件,...

    MyBatis分页功能实现

    此外,可以定义一个PageHelper类或自定义拦截器来实现更灵活的分页功能。 - **Mapper接口(UserMapper.java)**:定义与数据库交互的方法,如`selectUsersByPage`,该方法返回一个List并接收RowBounds作为参数。 ...

    Mybatis学习- 拦截器-实现分页

    在本主题中,我们将深入探讨如何利用Mybatis的拦截器机制来实现分页功能。 分页是Web应用中常见的需求,它能帮助用户更有效地浏览大量数据。Mybatis虽然没有内置的分页功能,但通过自定义拦截器,我们可以轻松地...

    mybatis拦截器分页

    总结来说,MyBatis 的拦截器机制为实现分页提供了一种高效、灵活的方式。通过拦截器,我们可以统一处理分页逻辑,降低代码复杂度,同时还能优化查询性能,提升开发效率。结合实际业务需求,正确配置和使用拦截器,能...

    MyBatis自动分页实现

    MyBatis 自动分页实现的关键是使用拦截器来拦截 SQL 语句并将其修改为分页查询语句。拦截器可以在执行 SQL 语句之前和之后进行拦截, таким образом,可以在执行 SQL 语句之前将其修改为分页查询语句,...

Global site tag (gtag.js) - Google Analytics