本来想用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;
}
}