mybatis 分页插件实现,自己动手丰衣足食

      本来想用PageHelper之类的第三方框架,仔细想想,还是自己实现一个。

/**
 * mybatis 分页插件
 * 
 * @author 戚辰先生
 *
 */
@PropertySource(value = { "classpath:mybaits.properties" })
@ConfigurationProperties(prefix = "mybaits")
@Intercepts(@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
		RowBounds.class, ResultHandler.class }))
public class MyBatisPageHelperInterceptor implements Interceptor {

	private static final Logger log = LoggerFactory.getLogger(MyBatisPageHelperInterceptor.class);

	private String countDialect;
	private String pageDialect;
	private String orderDialect;

	public Object intercept(Invocation invocation) throws Throwable {
		Object[] args = invocation.getArgs();
		MappedStatement ms = (MappedStatement) args[0];
		Object parameter = args[1];

		Pageable pageable = findPageable(parameter);
		if (pageable != null && !isResultLong(ms)) {
			log.debug("start page query [" + pageable + "]");
			long total = getTotalCount(ms, parameter);
			SqlSource sqlsource = new PageSqlSource(ms, parameter, pageable);
			args[0] = buildMappedStatement(ms, sqlsource);
			Object result = invocation.proceed();
			args[0] = ms;
			@SuppressWarnings("unchecked")
			PageList<Object> page = new PageList<Object>((List<Object>) result, pageable, total);
			return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), new Class<?>[] { List.class, Page.class },
					page);
		}

		return invocation.proceed();
	}

	private boolean isResultLong(MappedStatement ms) {
		for (ResultMap rmap : ms.getResultMaps()) {
			if (rmap.getType() == Long.class) {
				return true;
			}
		}
		return false;
	}

	private MappedStatement buildMappedStatement(MappedStatement ms, SqlSource sqlsource) {
		Builder builder = new Builder(ms.getConfiguration(), ms.getId(), sqlsource, ms.getSqlCommandType());
		builder.resource(ms.getResource());
		builder.fetchSize(ms.getFetchSize());
		builder.statementType(ms.getStatementType());
		builder.keyGenerator(ms.getKeyGenerator());
		builder.keyProperty(StringUtils.join(ms.getKeyProperties(), ','));
		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();
	}

	protected long getTotalCount(MappedStatement ms, Object parameter) throws Exception {
		int totpage = -1;
		BoundSql boundSql = ms.getBoundSql(parameter);
		String countSql = buildCountSql(boundSql.getSql().trim());
		BoundSql countbs = copyFromBoundSql(ms, boundSql, countSql);
		DefaultParameterHandler parameterHandler = new DefaultParameterHandler(ms, parameter, countbs);
		List<String> loginfo = new ArrayList<String>();
		for (String item : countSql.split("[\n\t ]")) {
			if (StringUtils.isNotBlank(item)) {
				loginfo.add(item.trim());
			}
		}
		log.debug("==>  Counting: " + StringUtils.join(loginfo, " "));
		DataSource ds = ms.getConfiguration().getEnvironment().getDataSource();
		Connection connection = null;
		PreparedStatement countStmt = null;
		ResultSet rs = null;
		try {
			connection = ds.getConnection();
			countStmt = connection.prepareStatement(countSql);
			parameterHandler.setParameters(countStmt);
			rs = countStmt.executeQuery();
			if (rs.next()) {
				totpage = rs.getInt(1);
			}
			log.debug("<==  TotalCount is " + totpage);
		} finally {
			if (rs != null)
				rs.close();
			if (countStmt != null)
				countStmt.close();
			if (connection != null)
				connection.close();
		}
		return totpage;
	}

	protected Pageable findPageable(Object params) {
		if (params == null)
			return null;
		if (params instanceof Map) {
			Map<Object, Object> clonemap = new HashMap<Object, Object>();
			for (Object key : ((Map<?, ?>) params).keySet()) {
				clonemap.put(key, ((Map<?, ?>) params).get(key));
			}
			params = clonemap;
		}
		JXPathContext context = JXPathContext.newContext(params);
		context.setLenient(true);
		try {

			for (String xpath : Arrays.asList(".", "page", "*/page")) {
				Object value = context.getValue(xpath);
				if (value != null && value instanceof Pageable) {
					return (Pageable) value;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private BoundSql copyFromBoundSql(MappedStatement ms, BoundSql boundSql, String sql) {
		BoundSql result = new BoundSql(ms.getConfiguration(), sql, boundSql.getParameterMappings(),
				boundSql.getParameterObject());
		for (ParameterMapping mapping : boundSql.getParameterMappings()) {
			String prop = mapping.getProperty();
			if (boundSql.hasAdditionalParameter(prop)) {
				result.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
			}
		}
		return result;
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	protected String buildCountSql(String oldsql) throws Exception {
		String dialect = getCountDialect();
		Assert.hasText(dialect, "CountDialect cannot empty");
		EvaluationContext context = new StandardEvaluationContext();
		context.setVariable("sql", oldsql);
		ExpressionParser paser = new SpelExpressionParser();
		Expression expression = paser.parseExpression(dialect, new TemplateParserContext());
		return expression.getValue(context, String.class);
	}

	protected String appendOrderSql(String oldsql, Sort sort) throws Exception {
		String dialect = getOrderDialect();
		Assert.hasText(dialect, "OrderDialect cannot empty");
		String result = oldsql;
		if (sort != null) {
			StringBuffer sb = new StringBuffer(" order by ");
			for (Order order : sort) {
				EvaluationContext context = new StandardEvaluationContext();
				context.setVariable("ignoreCase", order.isIgnoreCase());
				context.setVariable("column", order.getProperty());
				context.setVariable("column", order.isAscending());
				ExpressionParser paser = new SpelExpressionParser();
				Expression expression = paser.parseExpression(dialect, new TemplateParserContext());
				String ordersql = expression.getValue(context, String.class);
				sb.append(ordersql).append(",");
			}
			if (sb.charAt(sb.length() - 1) == ',') {
				sb.deleteCharAt(sb.length() - 1);
				result = result + sb.toString();
			}
		}
		return result;
	}

	protected String buildPageSql(String oldsql, Pageable page) throws Exception {
		String dialect = getPageDialect();
		Assert.hasText(dialect, "PageDialect cannot empty");

		if (orderDialect != null) {
			oldsql = appendOrderSql(oldsql, page.getSort());
		}
		int size = page.getPageSize();
		if (size == Integer.MAX_VALUE) {
			return oldsql;
		}
		EvaluationContext context = new StandardEvaluationContext();
		context.setVariable("sql", oldsql);
		context.setVariable("pageno", page.getOffset());
		context.setVariable("pagesize", size);
		ExpressionParser paser = new SpelExpressionParser();
		Expression expression = paser.parseExpression(dialect, new TemplateParserContext());
		return expression.getValue(context, String.class);
	}

	public void setProperties(Properties properties) {
		countDialect = properties.getProperty("countDialect");
		orderDialect = properties.getProperty("orderDialect");
		pageDialect = properties.getProperty("pageDialect");
	}

	private class PageSqlSource implements SqlSource {
		private MappedStatement ms;
		private Object parameter;
		private Pageable page;

		public PageSqlSource(MappedStatement ms, Object parameter, Pageable page) {
			this.ms = ms;
			this.parameter = parameter;
			this.page = page;
		}

		public BoundSql getBoundSql(Object parameterObject) {
			BoundSql boundSql = ms.getBoundSql(parameter);
			try {
				String pagesql = buildPageSql(boundSql.getSql(), page);
				return copyFromBoundSql(ms, boundSql, pagesql);
			} catch (Exception e) {
				throw new IllegalArgumentException(e);
			}
		}
	}

	private class PageList<T> implements InvocationHandler {

		private final Set<Method> listMethods = new HashSet<Method>(Arrays.asList(List.class.getMethods()));

		private Page<T> target;

		public PageList(List<T> content, Pageable pageable, long total) {
			target = new PageImpl<T>(content, pageable, total);
		}

		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
			if (listMethods.contains(method)) {
				return method.invoke(target.getContent(), args);
			} else {
				return method.invoke(target, args);
			}
		}
	}

	public String getCountDialect() {
		return countDialect;
	}

	public void setCountDialect(String countDialect) {
		this.countDialect = countDialect;
	}

	public String getPageDialect() {
		return pageDialect;
	}

	public void setPageDialect(String pageDialect) {
		this.pageDialect = pageDialect;
	}

	public String getOrderDialect() {
		return orderDialect;
	}

	public void setOrderDialect(String orderDialect) {
		this.orderDialect = orderDialect;
	}

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值