MyBatis Interceptor实现分页插件

来源:转载

MyBatis分页插件网上挺多的,基本都是基于plugin、Interceptor,当然我这个也不例外。基于每个人的表达方式不一样,万一我的特别符合某些人的胃口,就刚好给他们以启发了呢,所以还是记录下。


1、环境


环境随便弄,你看你自己怎么方便测试怎么弄。


新建maven项目,加入依赖:(mybatis、mysql驱动)


org.mybatismybatis3.4.1


mysqlmysql-connector-java5.1.25


测试下环境:


User.class:


public class User {private Integer id; private String nickname;private String email;


// setter getter


}


userMapper.xml





conf.xml





Test.class


public class MybatisTest {


SqlSession session;


@Beforepublic void openSession() { InputStream is = MybatisTest.class.getClassLoader().getResourceAsStream("hw/test/mybatis/conf.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is); session = factory.openSession();}


@Afterpublic void closeSession() { session.close();}


@Testpublic void getUser() { User u = session.selectOne("me.gacl.mapping.userMapper.getUser", 1); System.out.println("邮箱:"+u.getEmail());}


}


运行结果:


邮箱:[email protected]


ok~


2、编写分页插件代码:


org.apache.ibatis.plugin.Interceptor这个接口有三个方法:


1) Object intercept(Invocation invocation)是实现拦截逻辑的地方,内部要通过invocation.proceed()显式地推进责任链前进,也就是调用下一个拦截器拦截目标方法。


2) Object plugin(Object target) 就是用当前这个拦截器生成对目标target的代理,实际是通过Plugin.wrap(target,this) 来完成的,把目标target和拦截器this传给了包装函数。


3) setProperties(Properties properties)用于设置额外的参数,参数配置在拦截器的Properties节点里。


上面这个挺好理解的,看名字就明白了,这段解释我网上直接复制的,方便大家理解。


先上PageInterceptor代码:

@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }), @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) }) public class PageInterceptor implements Interceptor {ThreadLocal countTL = new ThreadLocal();ThreadLocal forPage = new ThreadLocal();


public Object intercept(Invocation invocation) throws Throwable { Object target = invocation.getTarget();


if (target instanceof StatementHandler) {MetaObject metaObject = SystemMetaObject.forObject(target);MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");String selectId = mappedStatement.getId();selectId = selectId.toLowerCase();// 是否是分页查询if (selectId.endsWith("page")) { forPage.set(true);} else { forPage.set(false); return invocation.proceed();}


StatementHandler statementHandler = (StatementHandler) target;BoundSql boundSql = statementHandler.getBoundSql();String sql = boundSql.getSql();StringBuilder buf = new StringBuilder("select count(*) from");sql = sql.toLowerCase();if (sql.indexOf("order") > sql.lastIndexOf(")")) { buf.append(sql.substring(sql.indexOf("from") + 4, sql.lastIndexOf("order")));} else { buf.append(sql.substring(sql.indexOf("from") + 4));}sql = buf.toString();Connection conn = (Connection) invocation.getArgs()[0];PreparedStatement stmt = conn.prepareStatement(sql);ResultSet rs = stmt.executeQuery();if (rs.next()) { int count = rs.getInt(1); countTL.set(count);}rs.close();stmt.close();return invocation.proceed(); } else if (target instanceof ResultSetHandler) {Boolean fp = forPage.get();if (fp != null && fp) { @SuppressWarnings("unchecked") List result = (List) invocation.proceed(); Page page = new Page(result); page.setCount(countTL.get()); return page;} } return invocation.proceed();}


public Object plugin(Object target) { return Plugin.wrap(target, this);}


public void setProperties(Properties arg0) {


} }

先讲讲 @Signature 这个注解,这个注解标注了要拦截那个方法: type 指定某个类,method指定方法,args指定参数类型列表。


Mybatis支持对Executor、StatementHandler、PameterHandler和ResultSetHandler进行拦截 ,所以说, type 只能为这四个中的一个,你具体想拦截哪个方法,可以点开源码copy下就好了。


在 PageInterceptor 中我拦截了 StatementHandler. prepare(Connection,Integer)方法,在这个方法中获取查询id(就是xml中的selec id),判断是否是分页查询,是分页则根据查询sql拼接count sql,使用jdbc查询出count值放入ThreadLocal中,等处理结果的时候在set到结果中。


Page.class


public class Page extends ArrayList implements Collection{private static final long serialVersionUID = 1L;


private int count; }


userMapper.xml



conf.xml


Test.class


@Testpublic void userPage() { RowBounds rowBounds = new RowBounds(10, 10); List<?> users = session.selectList("me.gacl.mapping.userMapper.userPage", null, rowBounds); @SuppressWarnings("unchecked") Page page = (Page) users; System.out.println("count:" + page.getCount() + ",users.size:" + users.size());}


运行结果:


count:15,users.size:5


ok~

以上都为测试代码,实际项目中需要根据具体情况编写,比如:组装count sql,就是简单的把查询列表替换成count,判断去掉sql末尾的order by。


分享给朋友:
您可能感兴趣的文章:
随机阅读: