接上一節(jié),增加一個(gè)分頁攔截器來實(shí)現(xiàn)dao層的分頁邏輯
攔截器就是在不改變源碼的情況下谐算,改變mybatis源碼的一些行為。在sql語句執(zhí)行之前归露,把普通sql語句換成分頁語句即可洲脂。
分析:怎樣攔截
(1)確定攔截對象:
a: 確定什么樣的攔截對象---后綴為Page的方法
b: 攔截對象什么行為---需要分頁操作
c: 什么時(shí)候攔截---在mybatis執(zhí)行sql語句之前攔截,準(zhǔn)確點(diǎn)是獲取statement之前剧包,在mybatis的源碼中
(2)攔截下來做什么事:代替某個(gè)方法完成分頁功能恐锦,改進(jìn)原始的查詢語句
(3)攔截結(jié)束,交回主權(quán)
做這個(gè)攔截器之前疆液,復(fù)習(xí)了一下動態(tài)代理一铅。我們的目的是在分頁sql執(zhí)行之前加上我們自己的分頁邏輯,而且多次用到分頁堕油,所以用動態(tài)代理非常合適潘飘。此處我們的PageInterceptor 就是自定義的執(zhí)行邏輯肮之,其他工作交給jdk的Proxy,讓它生成代理類卜录,讓代理類執(zhí)行分頁戈擒。
例子中mybatis版本3.4.0
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
*@Component: 注入到spring boot容器中
* @Intercepts分頁攔截器,type:要攔截的類的class對象暴凑,method:要攔截的方法峦甩,args:方法的參數(shù)
*/
@Component
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class, Integer.class})})
public class PageInterceptor implements Interceptor {
private String test;
private final static Logger logger = LoggerFactory.getLogger(PageInterceptor.class);
// 攔截實(shí)現(xiàn)
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
// 對statementHandler進(jìn)行封裝
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
// 配置文件中SQL語句的ID
String id = mappedStatement.getId();
if(id.matches(".+Page$")) {
BoundSql boundSql = statementHandler.getBoundSql();
// 原始的SQL語句
String sql = boundSql.getSql();
logger.info("原始sql:" + sql);
// 查詢總條數(shù)的SQL語句
String countSql = "select count(*) from (" + sql + ")a";
logger.info("查詢總條數(shù)sql:" + countSql);
Connection connection = (Connection)invocation.getArgs()[0];
PreparedStatement countStatement = connection.prepareStatement(countSql);
ParameterHandler parameterHandler = (ParameterHandler)metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(countStatement);
ResultSet rs = countStatement.executeQuery();
Map<?,?> parameter = (Map<?,?>)boundSql.getParameterObject();
Page page = (Page)parameter.get("page");
if(rs.next()) {
page.setTotalNumber(rs.getInt(1));
}
// 改造后帶分頁查詢的SQL語句
// String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
String pageSql = "select * from (select t1.*, rownum rn from (" + sql +
") t1 ) t2 where rn > " + page.getDbIndex() +" and rn <= " + page.getDbEnd();
logger.info("分頁sql:" + pageSql);
metaObject.setValue("delegate.boundSql.sql", pageSql);
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
logger.info(this.test);
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
mybatis源碼說明:target被代理對象,Plugin.wrap(target, this)返回的是動態(tài)生成的代理對象
getSignatureMap(interceptor):通過注解獲取要攔截的類现喳,方法等凯傲。
先通過setProperties拿到配置文件里的屬性值,再通過plugin方法對攔截的對象進(jìn)行過濾嗦篱,最后通過intercept方法執(zhí)行攔截的邏輯冰单。