PaginationInterceptor.java 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /**
  2. * Copyright (c) 2011-2020, hubin (jobob@qq.com).
  3. * <p>
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. * <p>
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. * <p>
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.baomidou.mybatisplus.plugins;
  17. import java.sql.Connection;
  18. import java.sql.PreparedStatement;
  19. import java.sql.ResultSet;
  20. import java.util.Properties;
  21. import org.apache.ibatis.executor.statement.StatementHandler;
  22. import org.apache.ibatis.logging.Log;
  23. import org.apache.ibatis.logging.LogFactory;
  24. import org.apache.ibatis.mapping.BoundSql;
  25. import org.apache.ibatis.mapping.MappedStatement;
  26. import org.apache.ibatis.mapping.SqlCommandType;
  27. import org.apache.ibatis.plugin.Interceptor;
  28. import org.apache.ibatis.plugin.Intercepts;
  29. import org.apache.ibatis.plugin.Invocation;
  30. import org.apache.ibatis.plugin.Plugin;
  31. import org.apache.ibatis.plugin.Signature;
  32. import org.apache.ibatis.reflection.MetaObject;
  33. import org.apache.ibatis.reflection.SystemMetaObject;
  34. import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
  35. import org.apache.ibatis.session.RowBounds;
  36. import com.baomidou.mybatisplus.MybatisDefaultParameterHandler;
  37. import com.baomidou.mybatisplus.enums.DBType;
  38. import com.baomidou.mybatisplus.parser.AbstractSqlParser;
  39. import com.baomidou.mybatisplus.parser.SqlInfo;
  40. import com.baomidou.mybatisplus.plugins.pagination.DialectFactory;
  41. import com.baomidou.mybatisplus.plugins.pagination.PageHelper;
  42. import com.baomidou.mybatisplus.plugins.pagination.Pagination;
  43. import com.baomidou.mybatisplus.toolkit.JdbcUtils;
  44. import com.baomidou.mybatisplus.toolkit.PluginUtils;
  45. import com.baomidou.mybatisplus.toolkit.SqlUtils;
  46. import com.baomidou.mybatisplus.toolkit.StringUtils;
  47. /**
  48. * <p>
  49. * 分页拦截器
  50. * </p>
  51. *
  52. * @author hubin
  53. * @Date 2016-01-23
  54. */
  55. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
  56. public class PaginationInterceptor implements Interceptor {
  57. // 日志
  58. private static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
  59. // COUNT SQL 解析
  60. private AbstractSqlParser sqlParser;
  61. /* 溢出总页数,设置第一页 */
  62. private boolean overflowCurrent = false;
  63. /* 方言类型 */
  64. private String dialectType;
  65. /* 方言实现类 */
  66. private String dialectClazz;
  67. /* 是否开启 PageHelper localPage 模式 */
  68. private boolean localPage = false;
  69. /**
  70. * Physical Pagination Interceptor for all the queries with parameter {@link org.apache.ibatis.session.RowBounds}
  71. */
  72. public Object intercept(Invocation invocation) throws Throwable {
  73. StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
  74. MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
  75. // 先判断是不是SELECT操作
  76. MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
  77. if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
  78. return invocation.proceed();
  79. }
  80. RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
  81. /* 不需要分页的场合 */
  82. if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
  83. // 本地线程分页
  84. if (localPage) {
  85. // 采用ThreadLocal变量处理的分页
  86. rowBounds = PageHelper.getPagination();
  87. if (rowBounds == null) {
  88. return invocation.proceed();
  89. }
  90. } else {
  91. // 无需分页
  92. return invocation.proceed();
  93. }
  94. }
  95. // 针对定义了rowBounds,做为mapper接口方法的参数
  96. BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
  97. String originalSql = boundSql.getSql();
  98. Connection connection = (Connection) invocation.getArgs()[0];
  99. DBType dbType = StringUtils.isNotEmpty(dialectType) ? DBType.getDBType(dialectType) : JdbcUtils.getDbType(connection.getMetaData().getURL());
  100. if (rowBounds instanceof Pagination) {
  101. Pagination page = (Pagination) rowBounds;
  102. boolean orderBy = true;
  103. if (page.isSearchCount()) {
  104. SqlInfo sqlInfo = SqlUtils.getCountOptimize(sqlParser, originalSql);
  105. orderBy = sqlInfo.isOrderBy();
  106. this.queryTotal(overflowCurrent, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
  107. if (page.getTotal() <= 0) {
  108. return invocation.proceed();
  109. }
  110. }
  111. String buildSql = SqlUtils.concatOrderBy(originalSql, page, orderBy);
  112. originalSql = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
  113. } else {
  114. // support physical Pagination for RowBounds
  115. originalSql = DialectFactory.buildPaginationSql(rowBounds, originalSql, dbType, dialectClazz);
  116. }
  117. /*
  118. * <p> 禁用内存分页 </p>
  119. * <p> 内存分页会查询所有结果出来处理(这个很吓人的),如果结果变化频繁这个数据还会不准。</p>
  120. */
  121. metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
  122. metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
  123. metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
  124. return invocation.proceed();
  125. }
  126. /**
  127. * 查询总记录条数
  128. *
  129. * @param sql
  130. * @param mappedStatement
  131. * @param boundSql
  132. * @param page
  133. */
  134. protected void queryTotal(boolean overflowCurrent, String sql, MappedStatement mappedStatement, BoundSql boundSql, Pagination page, Connection connection) {
  135. try (PreparedStatement statement = connection.prepareStatement(sql)) {
  136. DefaultParameterHandler parameterHandler = new MybatisDefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);
  137. parameterHandler.setParameters(statement);
  138. int total = 0;
  139. try (ResultSet resultSet = statement.executeQuery()) {
  140. if (resultSet.next()) {
  141. total = resultSet.getInt(1);
  142. }
  143. }
  144. page.setTotal(total);
  145. /*
  146. * 溢出总页数,设置第一页
  147. */
  148. int pages = page.getPages();
  149. if (overflowCurrent && (page.getCurrent() > pages)) {
  150. // 这里不能使用new,如果new内存地址会改变,导致前面的page无效
  151. // page = new Pagination(1, page.getSize());
  152. // page.setTotal(total);
  153. // 设置为第一条
  154. page.setCurrent(1);
  155. }
  156. } catch (Exception e) {
  157. logger.error("Error: Method queryTotal execution error !", e);
  158. }
  159. }
  160. public Object plugin(Object target) {
  161. if (target instanceof StatementHandler) {
  162. return Plugin.wrap(target, this);
  163. }
  164. return target;
  165. }
  166. public void setProperties(Properties prop) {
  167. String dialectType = prop.getProperty("dialectType");
  168. String dialectClazz = prop.getProperty("dialectClazz");
  169. if (StringUtils.isNotEmpty(dialectType)) {
  170. this.dialectType = dialectType;
  171. }
  172. if (StringUtils.isNotEmpty(dialectClazz)) {
  173. this.dialectClazz = dialectClazz;
  174. }
  175. }
  176. public void setDialectType(String dialectType) {
  177. this.dialectType = dialectType;
  178. }
  179. public void setDialectClazz(String dialectClazz) {
  180. this.dialectClazz = dialectClazz;
  181. }
  182. public void setOverflowCurrent(boolean overflowCurrent) {
  183. this.overflowCurrent = overflowCurrent;
  184. }
  185. public void setSqlParser(AbstractSqlParser sqlParser) {
  186. this.sqlParser = sqlParser;
  187. }
  188. public void setLocalPage(boolean localPage) {
  189. this.localPage = localPage;
  190. }
  191. }