45. return totalPage; 46. } 47.
48. public void setTotalPage(int totalPage) { 49. this.totalPage = totalPage; 50. } 51.
52. public List
56. public void setResults(List
60. public Map
64. public void setParams(Map
68. @Override
69. public String toString() {
70. StringBuilder builder = new StringBuilder();
71. builder.append(\72. .append(pageSize).append(\73. \74. \75. return builder.toString(); 76. } 77. 78. }
对于需要进行分页的Mapper映射,我们会给它传一个Page对象作为参数,我们可以看到Page对象里面包括了一些分页的基本信息,这些信息我们可以在拦截器里面用到,然后我们把除分页的基本信息以外的其他参数用一个Map对象进行包装,这样在Mapper映射语句中的其他参数就可以从Map中取值了。接着来看一下我们的PageInterceptor的定义,对于PageInterceptor我就不做过多的说明,代码里面附有很详细的注释信息: Java代码
1. package com.tiantian.mybatis.interceptor; 2.
3. import java.lang.reflect.Field; 4. import java.sql.Connection; 5. import java.sql.PreparedStatement; 6. import java.sql.ResultSet; 7. import java.sql.SQLException; 8. import java.util.List; 9. import java.util.Properties; 10.
11. import org.apache.ibatis.executor.parameter.ParameterHandler; 12. import org.apache.ibatis.executor.statement.RoutingStatementHandler; 13. import org.apache.ibatis.executor.statement.StatementHandler; 14. import org.apache.ibatis.mapping.BoundSql; 15. import org.apache.ibatis.mapping.MappedStatement; 16. import org.apache.ibatis.mapping.ParameterMapping; 17. import org.apache.ibatis.plugin.Interceptor; 18. import org.apache.ibatis.plugin.Intercepts; 19. import org.apache.ibatis.plugin.Invocation; 20. import org.apache.ibatis.plugin.Plugin; 21. import org.apache.ibatis.plugin.Signature;
22. import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; 23.
24. import com.tiantian.mybatis.model.Page; 25. 26. /** 27. *
28. * 分页拦截器,用于拦截需要进行分页查询的操作,然后对其进行分页处理。 29. * 利用拦截器实现Mybatis分页的原理:
30. * 要利用JDBC对数据库进行操作就必须要有一个对应的Statement对象,Mybatis
在执行Sql语句前就会产生一个包含Sql语句的Statement对象,而且对应的Sql语句
31. * 是在Statement之前产生的,所以我们就可以在它生成Statement之前对用来生
成Statement的Sql语句下手。在Mybatis中Statement语句是通过RoutingStatementHandler对象的
32. * prepare方法生成的。所以利用拦截器实现Mybatis分页的一个思路就是拦截
StatementHandler接口的prepare方法,然后在拦截器方法中把Sql语句改成对应的分页查询Sql语句,之后再调用
33. * StatementHandler对象的prepare方法,即调用invocation.proceed()。 34. * 对于分页而言,在拦截器里面我们还需要做的一个操作就是统计满足当前条件的
记录一共有多少,这是通过获取到了原始的Sql语句后,把它改为对应的统计语句再利用Mybatis封装好的参数和设
35. * 置参数的功能把Sql语句中的参数进行替换,之后再执行查询记录数的Sql语句
进行总记录数的统计。 36. * 37. */
38. @Intercepts( {
39. @Signature(method = \
onnection.class}) })
40. public class PageInterceptor implements Interceptor { 41.
42. private String databaseType;//数据库类型,不同的数据库有不同的分页方法 43. 44. /**
45. * 拦截后要执行的方法 46. */
47. public Object intercept(Invocation invocation) throws Throwable { 48. //对于StatementHandler其实只有两个实现类,一个是
RoutingStatementHandler,另一个是抽象类BaseStatementHandler, 49. //BaseStatementHandler有三个子类,分别是SimpleStatementHandler,
PreparedStatementHandler和CallableStatementHandler, 50. //SimpleStatementHandler是用于处理Statement的,
PreparedStatementHandler是处理PreparedStatement的,而CallableStatementHandler是
51. //处理CallableStatement的。Mybatis在进行Sql语句处理的时候都是建立的
RoutingStatementHandler,而在RoutingStatementHandler里面拥有一个
52. //StatementHandler类型的delegate属性,RoutingStatementHandler会依据
Statement的不同建立对应的BaseStatementHandler,即SimpleStatementHandler、
53. //PreparedStatementHandler或CallableStatementHandler,在
RoutingStatementHandler里面所有StatementHandler接口方法的实现都是调用的delegate对应的方法。
54. //我们在PageInterceptor类上已经用@Signature标记了该Interceptor只拦截
StatementHandler接口的prepare方法,又因为Mybatis只有在建立RoutingStatementHandler的时候
55. //是通过Interceptor的plugin方法进行包裹的,所以我们这里拦截到的目标对
象肯定是RoutingStatementHandler对象。
56. RoutingStatementHandler handler = (RoutingStatementHandler) invocation.
getTarget();
57. //通过反射获取到当前RoutingStatementHandler对象的delegate属性 58. StatementHandler delegate = (StatementHandler)ReflectUtil.getFieldValue(h
andler, \
59. //获取到当前StatementHandler的 boundSql,这里不管是调用
handler.getBoundSql()还是直接调用delegate.getBoundSql()结果是一样的,因为之前已经说过了
60. //RoutingStatementHandler实现的所有StatementHandler接口方法里面都是
调用的delegate对应的方法。
61. BoundSql boundSql = delegate.getBoundSql();
62. //拿到当前绑定Sql的参数对象,就是我们在调用对应的Mapper映射语句时所
传入的参数对象
63. Object obj = boundSql.getParameterObject();
64. //这里我们简单的通过传入的是Page对象就认定它是需要进行分页操作的。 65. if (obj instanceof Page>) { 66. Page> page = (Page>) obj;
67. //通过反射获取delegate父类BaseStatementHandler的mappedStatement
属性
68. MappedStatement mappedStatement = (MappedStatement)ReflectUtil.ge
tFieldValue(delegate, \
69. //拦截到的prepare方法参数是一个Connection对象
70. Connection connection = (Connection)invocation.getArgs()[0]; 71. //获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的
Sql语句
72. String sql = boundSql.getSql();
73. //给当前的page参数对象设置总记录数 74. this.setTotalRecord(page,
75. mappedStatement, connection); 76. //获取分页Sql语句
77. String pageSql = this.getPageSql(page, sql);
78. //利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语
句
79. ReflectUtil.setFieldValue(boundSql, \80. }
81. return invocation.proceed(); 82. } 83. 84. 85. /**
86. * 拦截器对应的封装原始对象的方法 87. */
88. public Object plugin(Object target) { 89. return Plugin.wrap(target, this); 90. } 91. 92. /**
93. * 设置注册拦截器时设定的属性 94. */
95. public void setProperties(Properties properties) {
96. this.databaseType = properties.getProperty(\97. } 98. 99. /**
100. * 根据page对象获取对应的分页查询Sql语句,这里只做了两种数据库类型,
Mysql和Oracle
101. * 其它的数据库都 没有进行分页 102. *
103. * @param page 分页对象 104. * @param sql 原sql语句 105. * @return 106. */
107. private String getPageSql(Page> page, String sql) {