Java tutorial
/** * @(#){PageInterceptor}.java 1.0 {15/12/26} * * Copyright 2015 greatpwx@126.com, All rights reserved. * Use is subject to license terms. * https://github.com/monee1988/SpringMybatisWebInterceptor */ package com.monee1988.core.mybatis.pageinterceptor; import cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor; import cn.org.rapid_framework.jdbc.dialect.Dialect; import com.monee1988.core.entity.Page; import org.apache.ibatis.executor.CachingExecutor; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.MappedStatement.Builder; import org.apache.ibatis.mapping.ParameterMapping; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.plugin.Interceptor; import org.apache.ibatis.plugin.Intercepts; import org.apache.ibatis.plugin.Invocation; import org.apache.ibatis.plugin.Signature; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.apache.ibatis.transaction.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.ReflectionUtils; import java.lang.reflect.Field; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; /** * * OffsetLimitInterceptor * * <pre> * <property name="plugins"> * <array> * <!-- --> * <bean class="com.platform.mybatis.pageinterceptor.PageInterceptor"> * <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/> * </bean> * </array> * </property> * </pre> * * @author codePwx * @time 2015-12-21 * */ @Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }) }) public class PageInterceptor extends OffsetLimitInterceptor implements Interceptor { private Logger logger = LoggerFactory.getLogger(getClass()); protected static final String PAGE = "page"; protected static int MAPPED_STATEMENT_INDEX = 0; protected static int PARAMETER_INDEX = 1; protected static int ROWBOUNDS_INDEX = 2; protected static int RESULT_HANDLER_INDEX = 3; protected Dialect dialect; public Object intercept(Invocation invocation) throws Throwable { processMybatisIntercept(invocation.getArgs(), invocation); return invocation.proceed(); } void processMybatisIntercept(final Object[] queryArgs, Invocation invocation) { MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX]; Object parameter = queryArgs[PARAMETER_INDEX]; Page<?> page = null; if (parameter != null) { page = convertParameter(page, parameter); } if (dialect.supportsLimit() && page != null) { BoundSql boundSql = ms.getBoundSql(parameter); String sql = boundSql.getSql().trim(); final RowBounds rowBounds = (RowBounds) queryArgs[ROWBOUNDS_INDEX]; int offset = rowBounds.getOffset(); int limit = rowBounds.getLimit(); offset = page.getOffset(); limit = page.getPageSize(); CachingExecutor executor = (CachingExecutor) invocation.getTarget(); Transaction transaction = executor.getTransaction(); try { Connection connection = transaction.getConnection(); /** * */ this.setTotalRecord(page, ms, connection, parameter); } catch (SQLException e) { e.printStackTrace(); } if (dialect.supportsLimitOffset()) { sql = dialect.getLimitString(sql, offset, limit); offset = RowBounds.NO_ROW_OFFSET; } else { sql = dialect.getLimitString(sql, 0, limit); } limit = RowBounds.NO_ROW_LIMIT; queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset, limit); BoundSql newBoundSql = copyFromBoundSql(ms, boundSql, sql); MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql)); queryArgs[MAPPED_STATEMENT_INDEX] = newMs; } } /** * * @param page * @param mappedStatement * @param connection * @param parameterObject */ private void setTotalRecord(Page<?> page, MappedStatement mappedStatement, Connection connection, Object parameterObject) { BoundSql boundSql = mappedStatement.getBoundSql(parameterObject); String sql = boundSql.getSql(); String countSql = removeBreakingWhitespace(this.getCountSql(sql)); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, parameterObject); ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, countBoundSql); PreparedStatement pstmt = null; ResultSet rs = null; logger.debug("Total count SQL [{}] ", countSql.toString()); logger.debug("Total count Parameters: {} ", parameterObject); try { pstmt = connection.prepareStatement(countSql); parameterHandler.setParameters(pstmt); rs = pstmt.executeQuery(); if (rs.next()) { int totalRecord = rs.getInt(1); logger.debug("Total count: {}", totalRecord); page.setTotalCount(totalRecord); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (pstmt != null) pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * @param sql * @return */ private String getCountSql(String sql) { return "select count(1) from (" + sql + ") AS total"; } /** * @param page * @param parameter * @return */ private Page<?> convertParameter(Page<?> page, Object parameter) { if (parameter instanceof Page<?>) { return (Page<?>) parameter; } if (parameter instanceof Map<?, ?>) { return (Page<?>) ((Map<?, ?>) parameter).get(PAGE); } return currentGetFiled(parameter, PAGE); } private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql, String sql) { BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(), boundSql.getParameterObject()); for (ParameterMapping mapping : boundSql.getParameterMappings()) { String prop = mapping.getProperty(); if (boundSql.hasAdditionalParameter(prop)) { newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop)); } } return newBoundSql; } // see: MapperBuilderAssistant private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) { Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); } @Override public void setProperties(Properties properties) { super.setProperties(properties); } /** * @param dialectClass * the dialectClass to set */ public void setDialectClass(String dialectClass) { try { dialect = (Dialect) Class.forName(dialectClass).newInstance(); } catch (Exception e) { throw new RuntimeException("cannot create dialect instance by dialectClass:" + dialectClass, e); } logger.debug(this.getClass().getSimpleName() + ".dialect=[{}]", dialect.getClass().getSimpleName()); } /** * @return * */ private static Page<?> currentGetFiled(Object object, String param) { Field pageField = ReflectionUtils.findField(object.getClass(), param); try { boolean accessible = pageField.isAccessible(); pageField.setAccessible(Boolean.TRUE); Page<?> page = (Page<?>) pageField.get(object); pageField.setAccessible(accessible); if (page != null) { return page; } } catch (Exception e) { return null; } return null; } protected String removeBreakingWhitespace(String original) { StringTokenizer whitespaceStripper = new StringTokenizer(original); StringBuilder builder = new StringBuilder(); while (whitespaceStripper.hasMoreTokens()) { builder.append(whitespaceStripper.nextToken()); builder.append(" "); } return builder.toString(); } }