本文的程序基本上都不是我自己实现的,网上有好多现成的例子。我只是根据自己理解,对整个过程进行了说明。
Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最好了解下它的原理,以便写出安全高效的插件。Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截,也就是说会对这4种对象进行代理。
对于分页,通过Mybatis学习- 拦截器-基本的图中,可以看到比较适合在StatementHandler上进行拦截。这样,我们通过获得其boundsql对象(就是sql语句+查询参数 的对象);然后判断boundsql对象的参数中有没有分页信息,如果有,则把分页信息取出来,然后重新组织boundsql的sql语句(因为,分页信息是动态的,所以对于mybatis来说这部分的参数mybatis处理不了,所以我们要取出来,然后根据这些信息去修改boundsql中的sql部分)。
好了,根据上面的思路,首先我们要获得StatementHandler:
if (ivk.getTarget() instanceof RoutingStatementHandler) {
RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk
.getTarget();
BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper
.getValueByFieldName(statementHandler, "delegate");
上面这个代码,表示我们首先获得RoutingStatementHandler,这个Handler也是继承了StatementHandler接口,但是基本不做具体的事情,只是再把请求转发给其他的StatementHandler。默认的BaseStatementHandler(具体执行查询的handler)怎样获得? 用delegate!就是说RoutingStatementHandler的delegate成员。
得到了BaseStatementHandler,你就可以在这上面做些事情了。获取boundsql,然后修改boundsql:
Object parameterObject = boundSql.getParameterObject();
获取boundsql的参数,执行某些检查,看看里面有没有包含分页参数?
没有,则return ivk.proceed(); 继续执行链;
有,则需要根据分页信息,修改boundsql中的sql,然后return ivk.proceed();
这里修改sql有点技巧,因为不同的rdbms分页的语法都有些不同,所以我们实现诸如下面的代码:
private String generatePagesSql(String sql, PageView page) {
if (page != null && dialectObject != null) {
//pageNow默认是从1,而已数据库是从0开始计算的.所以(page.getPageNow()-1)
int pageNow = page.getPageNow();
return dialectObject.getLimitString(sql, (pageNow<=0?0:pageNow-1)
* page.getPageSize(), page.getPageSize());
}
return sql;
}
让具体重新拼装sql的工作交给dialectObject.getLimitString(sql, (pageNow<=0?0:pageNow-1) * page.getPageSize(), page.getPageSize());去做。
然后我们根据不同的rdbms实现不同的dialectObject.getLimitString就可以了。
好,现在总结一下。
你想实现分页,获取StatementHandler-> BOUNDSQL-->加工boundsql-->return ivk.proceed();
下面提出一个问题: 如果我们已经实现了处理链,对请求进行拦截,并根据请求的参数进行处理,那么我们需要发什么样的请求?
就是说我们的Mapper文件应该怎样写?我们的Mapper接口应该怎样写?我们调用的时候应该传什么参数?
先看Mapper文件:
<select id="query" resultMap="BaseResultMap" parameterType="java.util.HashMap" >
select
*
from bai_user
<where>
<if test="t.USERNAME != null and t.USERNAME != ''">
username like '%${t.USERNAME}%'
</if>
</where>
</select>
parameterType应该是一个Map类型,这样我们可以定义一个pageview,然后放在里面,到了boundsql中在根据名字取出来。
再看Mapper接口:
Mapper文件中我们传递的是Map,但我们Map中的参数类型不确定,有的是数值,有的是字符串,有的是pageview对象。所以变成如下格式:
List<baiuser> query(HashMap<Object, Object> map);
再看怎样调用。写一个测试类:
package example10.test;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import org.junit.Test;
import org.junit.Before;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.alibaba.fastjson.JSON;
import example10.dao.baiuserMapper;
import example10.model.baiuser;
import example10.util.PageView;
public class baiuserTest {
private baiuserMapper userDao;
@Before
public void before(){
@SuppressWarnings("resource")
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:example10/conf/spring.xml"
});
userDao = (baiuserMapper) context.getBean("baiuserMapper");
}
@Test
public void test(){
HashMap<Object, Object> map = new HashMap<Object, Object>();
baiuser user1 = new baiuser();
user1.setUSERNAME("bai");
PageView pageView = new PageView();
map.put("paging", pageView);
map.put("t", user1);
//return getSqlSession().selectList(baiuserMapper.query ,map);
List<baiuser> user = userDao.query(map);
System.out.println(JSON.toJSONString(user));
}
}
示例代码:
http://download.csdn.net/detail/u013269938/7302953