1. package com.tiantian.mybatis.interceptor; 2.
3. import java.sql.Connection; 4. import java.util.Properties; 5.
6. import org.apache.ibatis.executor.Executor;
7. import org.apache.ibatis.executor.statement.StatementHandler; 8. import org.apache.ibatis.mapping.MappedStatement; 9. import org.apache.ibatis.plugin.Interceptor; 10. import org.apache.ibatis.plugin.Intercepts; 11. import org.apache.ibatis.plugin.Invocation; 12. import org.apache.ibatis.plugin.Plugin; 13. import org.apache.ibatis.plugin.Signature; 14. import org.apache.ibatis.session.ResultHandler; 15. import org.apache.ibatis.session.RowBounds; 16.
17. @Intercepts( {
18. @Signature(method = \19. MappedStatement.class, Object.class, RowBounds.class, 20. ResultHandler.class }),
21. @Signature(method = \
onnection.class }) })
22. public class MyInterceptor implements Interceptor { 23.
24. public Object intercept(Invocation invocation) throws Throwable { 25. Object result = invocation.proceed(); 26. System.out.println(\27. return result; 28. } 29.
30. public Object plugin(Object target) { 31. return Plugin.wrap(target, this); 32. } 33.
34. public void setProperties(Properties properties) { 35. String prop1 = properties.getProperty(\36. String prop2 = properties.getProperty(\
37. System.out.println(prop1 + \38. } 39. 40. }
首先看setProperties方法,这个方法在Configuration初始化当前的Interceptor时就会执行,这里只是简单的取两个属性进行打印。
其次看plugin方法中我们是用的Plugin的逻辑来实现Mybatis的逻辑的。 接着看MyInterceptor类上我们用@Intercepts标记了这是一个Interceptor,然后在@Intercepts中定义了两个@Signature,即两个拦截点。第一个@Signature我们定义了该Interceptor将拦截Executor接口中参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法;第二个@Signature我们定义了该Interceptor将拦截StatementHandler中参数类型为Connection的prepare方法。
最后再来看一下intercept方法,这里我们只是简单的打印了一句话,然后调用invocation的proceed方法,使当前方法正常的调用。
对于这个拦截器,Mybatis在注册该拦截器的时候就会利用定义好的n个property作为参数调用该拦截器的setProperties方法。之后在新建可拦截对象的时候会调用该拦截器的plugin方法来决定是返回目标对象本身还是代理对象。对于这个拦截器而言,当Mybatis是要Executor或StatementHandler对象的时候就会返回一个代理对象,其他都是原目标对象本身。然后当Executor代理对象在执行参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法或StatementHandler代理对象在执行参数类型为Connection的prepare方法时就会触发当前的拦截器的intercept方法进行拦截,而执行这两个接口对象的其他方法时都只是做一个简单的代理。
1.4 注册拦截器
注册拦截器是通过在Mybatis配置文件中plugins元素下的plugin元素来进行的。一个plugin对应着一个拦截器,在plugin元素下面我们可以指定若干个property子元素。Mybatis在注册定义的拦截器时会先把对应拦截器下面的所有property通过Interceptor的setProperties方法注入给对应的拦截器。所以,我们可以这样来注册我们在前面定义的MyInterceptor:
Xml代码
1.
3. PUBLIC \4. \5.
6.
8.
11.
16. 22.
28.
1.5 Mybatis可拦截的方法
Mybatis拦截器只能拦截四种类型的接口:Executor、StatementHandler、
ParameterHandler和ResultSetHandler。这是在Mybatis的Configuration中写死了的,如果要支持拦截其他接口就需要我们重写Mybatis的Configuration。Mybatis可以对这四个接口中所有的方法进行拦截。
1.6 利用拦截器进行分页
下面将介绍一个Mybatis拦截器的实际应用。Mybatis拦截器常常会被用来进行分页处理。我们知道要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis在执行Sql语句前也会产生一个包含Sql语句的Statement对象,而且对应的Sql语句是在Statement之前产生的,所以我们就可以在它成Statement之前对用来生成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用StatementHandler对象的prepare方法,即调用
invocation.proceed()。更改Sql语句这个看起来很简单,而事实上来说的话就没那么直观,因为包括sql等其他属性在内的多个属性都没有对应的方法可以直接取到,它们对外部都是封闭的,是对象的私有属性,所以这里就需要引入反射机制来获取或者更改对象的私有属性的值了。对于分页而言,在拦截器里面我们常常还需要做的一个操作就是统计满足当前条件的记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句进行总记录数的统计。先来看一个我们对分页操作封装的一个实体类Page: Java代码
1. import java.util.HashMap; 2. import java.util.List; 3. import java.util.Map; 4. 5. /**
6. * 对分页的基本数据进行一个简单的封装 7. */
8. public class Page
10. private int pageNo = 1;//页码,默认是第一页
11. private int pageSize = 15;//每页显示的记录数,默认是15 12. private int totalRecord;//总记录数 13. private int totalPage;//总页数
14. private List
15. private Map
他的参数我们把它分装成一个Map对象 16.
17. public int getPageNo() { 18. return pageNo; 19. } 20.
21. public void setPageNo(int pageNo) { 22. this.pageNo = pageNo; 23. } 24.
25. public int getPageSize() { 26. return pageSize; 27. } 28.
29. public void setPageSize(int pageSize) { 30. this.pageSize = pageSize; 31. } 32.
33. public int getTotalRecord() { 34. return totalRecord; 35. } 36.
37. public void setTotalRecord(int totalRecord) { 38. this.totalRecord = totalRecord;
39. //在设置总页数的时候计算出对应的总页数,在下面的三目运算中加法拥有更
高的优先级,所以最后可以不加括号。
40. int totalPage = totalRecord%pageSize==0 ? totalRecord/pageSize : totalRec
ord/pageSize + 1;
41. this.setTotalPage(totalPage); 42. } 43.
44. public int getTotalPage() {