PaginationInterceptor.java 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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.plugins.pagination.DialectFactory;
  39. import com.baomidou.mybatisplus.plugins.pagination.PageHelper;
  40. import com.baomidou.mybatisplus.plugins.pagination.Pagination;
  41. import com.baomidou.mybatisplus.plugins.parser.ISqlParser;
  42. import com.baomidou.mybatisplus.plugins.parser.SqlInfo;
  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 extends SqlParserHandler implements Interceptor {
  57. // 日志
  58. private static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
  59. // COUNT SQL 解析
  60. private ISqlParser 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. @Override
  73. public Object intercept(Invocation invocation) throws Throwable {
  74. StatementHandler statementHandler = (StatementHandler) PluginUtils.realTarget(invocation.getTarget());
  75. MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
  76. this.sqlParser(metaObject);
  77. // 先判断是不是SELECT操作
  78. MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
  79. if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
  80. return invocation.proceed();
  81. }
  82. RowBounds rowBounds = (RowBounds) metaObject.getValue("delegate.rowBounds");
  83. /* 不需要分页的场合 */
  84. if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
  85. // 本地线程分页
  86. if (localPage) {
  87. // 采用ThreadLocal变量处理的分页
  88. rowBounds = PageHelper.getPagination();
  89. if (rowBounds == null) {
  90. return invocation.proceed();
  91. }
  92. } else {
  93. // 无需分页
  94. return invocation.proceed();
  95. }
  96. }
  97. // 针对定义了rowBounds,做为mapper接口方法的参数
  98. BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql");
  99. String originalSql = boundSql.getSql();
  100. Connection connection = (Connection) invocation.getArgs()[0];
  101. DBType dbType = StringUtils.isNotEmpty(dialectType) ? DBType.getDBType(dialectType) : JdbcUtils.getDbType(connection.getMetaData().getURL());
  102. if (rowBounds instanceof Pagination) {
  103. Pagination page = (Pagination) rowBounds;
  104. boolean orderBy = true;
  105. if (page.isSearchCount()) {
  106. SqlInfo sqlInfo = SqlUtils.getCountOptimize(sqlParser, originalSql);
  107. orderBy = sqlInfo.isOrderBy();
  108. this.queryTotal(overflowCurrent, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
  109. if (page.getTotal() <= 0) {
  110. return invocation.proceed();
  111. }
  112. }
  113. String buildSql = SqlUtils.concatOrderBy(originalSql, page, orderBy);
  114. originalSql = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
  115. } else {
  116. // support physical Pagination for RowBounds
  117. originalSql = DialectFactory.buildPaginationSql(rowBounds, originalSql, dbType, dialectClazz);
  118. }
  119. /*
  120. * <p> 禁用内存分页 </p>
  121. * <p> 内存分页会查询所有结果出来处理(这个很吓人的),如果结果变化频繁这个数据还会不准。</p>
  122. */
  123. metaObject.setValue("delegate.boundSql.sql", originalSql);
  124. metaObject.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
  125. metaObject.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
  126. return invocation.proceed();
  127. }
  128. /**
  129. * 查询总记录条数
  130. *
  131. * @param sql
  132. * @param mappedStatement
  133. * @param boundSql
  134. * @param page
  135. */
  136. protected void queryTotal(boolean overflowCurrent, String sql, MappedStatement mappedStatement, BoundSql boundSql, Pagination page, Connection connection) {
  137. try (PreparedStatement statement = connection.prepareStatement(sql)) {
  138. DefaultParameterHandler parameterHandler = new MybatisDefaultParameterHandler(mappedStatement, boundSql.getParameterObject(), boundSql);
  139. parameterHandler.setParameters(statement);
  140. int total = 0;
  141. try (ResultSet resultSet = statement.executeQuery()) {
  142. if (resultSet.next()) {
  143. total = resultSet.getInt(1);
  144. }
  145. }
  146. page.setTotal(total);
  147. /*
  148. * 溢出总页数,设置第一页
  149. */
  150. int pages = page.getPages();
  151. if (overflowCurrent && (page.getCurrent() > pages)) {
  152. // 设置为第一条
  153. page.setCurrent(1);
  154. }
  155. } catch (Exception e) {
  156. logger.error("Error: Method queryTotal execution error !", e);
  157. }
  158. }
  159. @Override
  160. public Object plugin(Object target) {
  161. if (target instanceof StatementHandler) {
  162. return Plugin.wrap(target, this);
  163. }
  164. return target;
  165. }
  166. @Override
  167. public void setProperties(Properties prop) {
  168. String dialectType = prop.getProperty("dialectType");
  169. String dialectClazz = prop.getProperty("dialectClazz");
  170. String localPage = prop.getProperty("localPage");
  171. if (StringUtils.isNotEmpty(dialectType)) {
  172. this.dialectType = dialectType;
  173. }
  174. if (StringUtils.isNotEmpty(dialectClazz)) {
  175. this.dialectClazz = dialectClazz;
  176. }
  177. if (StringUtils.isNotEmpty(localPage)) {
  178. this.localPage = Boolean.valueOf(localPage);
  179. }
  180. }
  181. public PaginationInterceptor setDialectType(String dialectType) {
  182. this.dialectType = dialectType;
  183. return this;
  184. }
  185. public PaginationInterceptor setDialectClazz(String dialectClazz) {
  186. this.dialectClazz = dialectClazz;
  187. return this;
  188. }
  189. public PaginationInterceptor setOverflowCurrent(boolean overflowCurrent) {
  190. this.overflowCurrent = overflowCurrent;
  191. return this;
  192. }
  193. public PaginationInterceptor setSqlParser(ISqlParser sqlParser) {
  194. this.sqlParser = sqlParser;
  195. return this;
  196. }
  197. public PaginationInterceptor setLocalPage(boolean localPage) {
  198. this.localPage = localPage;
  199. return this;
  200. }
  201. }