比較適用于在分頁時(shí)候進(jìn)行攔截。對(duì)分頁的SQL語句通過封裝處理,處理成不同的分頁sql。
成都創(chuàng)新互聯(lián)公司一直秉承“誠信做人,踏實(shí)做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個(gè)客戶多一個(gè)朋友!為您提供成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都網(wǎng)頁設(shè)計(jì)、小程序開發(fā)、成都網(wǎng)站開發(fā)、成都網(wǎng)站制作、成都軟件開發(fā)、重慶APP開發(fā)公司是成都本地專業(yè)的網(wǎng)站建設(shè)和網(wǎng)站設(shè)計(jì)公司,等你一起來見證!
實(shí)用性比較強(qiáng)。
import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.RoutingStatementHandler; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.ParameterMapping; 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.scripting.defaults.DefaultParameterHandler; import com.yidao.utils.Page; import com.yidao.utils.ReflectHelper; /** * * 分頁攔截器,用于攔截需要進(jìn)行分頁查詢的操作,然后對(duì)其進(jìn)行分頁處理。 * 利用攔截器實(shí)現(xiàn)Mybatis分頁的原理: * 要利用JDBC對(duì)數(shù)據(jù)庫進(jìn)行操作就必須要有一個(gè)對(duì)應(yīng)的Statement對(duì)象,Mybatis在執(zhí)行Sql語句前就會(huì)產(chǎn)生一個(gè)包含Sql語句的Statement對(duì)象,而且對(duì)應(yīng)的Sql語句 * 是在Statement之前產(chǎn)生的,所以我們就可以在它生成Statement之前對(duì)用來生成Statement的Sql語句下手。在Mybatis中Statement語句是通過RoutingStatementHandler對(duì)象的 * prepare方法生成的。所以利用攔截器實(shí)現(xiàn)Mybatis分頁的一個(gè)思路就是攔截StatementHandler接口的prepare方法,然后在攔截器方法中把Sql語句改成對(duì)應(yīng)的分頁查詢Sql語句,之后再調(diào)用 * StatementHandler對(duì)象的prepare方法,即調(diào)用invocation.proceed()。 * 對(duì)于分頁而言,在攔截器里面我們還需要做的一個(gè)操作就是統(tǒng)計(jì)滿足當(dāng)前條件的記錄一共有多少,這是通過獲取到了原始的Sql語句后,把它改為對(duì)應(yīng)的統(tǒng)計(jì)語句再利用Mybatis封裝好的參數(shù)和設(shè) * 置參數(shù)的功能把Sql語句中的參數(shù)進(jìn)行替換,之后再執(zhí)行查詢記錄數(shù)的Sql語句進(jìn)行總記錄數(shù)的統(tǒng)計(jì)。 * */ @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})}) public class PageInterceptor implements Interceptor { private String dialect = ""; //數(shù)據(jù)庫方言 private String pageSqlId = ""; //mapper.xml中需要攔截的ID(正則匹配) public Object intercept(Invocation invocation) throws Throwable { //對(duì)于StatementHandler其實(shí)只有兩個(gè)實(shí)現(xiàn)類,一個(gè)是RoutingStatementHandler,另一個(gè)是抽象類BaseStatementHandler, //BaseStatementHandler有三個(gè)子類,分別是SimpleStatementHandler,PreparedStatementHandler和CallableStatementHandler, //SimpleStatementHandler是用于處理Statement的,PreparedStatementHandler是處理PreparedStatement的,而CallableStatementHandler是 //處理CallableStatement的。Mybatis在進(jìn)行Sql語句處理的時(shí)候都是建立的RoutingStatementHandler,而在RoutingStatementHandler里面擁有一個(gè) //StatementHandler類型的delegate屬性,RoutingStatementHandler會(huì)依據(jù)Statement的不同建立對(duì)應(yīng)的BaseStatementHandler,即SimpleStatementHandler、 //PreparedStatementHandler或CallableStatementHandler,在RoutingStatementHandler里面所有StatementHandler接口方法的實(shí)現(xiàn)都是調(diào)用的delegate對(duì)應(yīng)的方法。 //我們?cè)赑ageInterceptor類上已經(jīng)用@Signature標(biāo)記了該Interceptor只攔截StatementHandler接口的prepare方法,又因?yàn)镸ybatis只有在建立RoutingStatementHandler的時(shí)候 //是通過Interceptor的plugin方法進(jìn)行包裹的,所以我們這里攔截到的目標(biāo)對(duì)象肯定是RoutingStatementHandler對(duì)象。 if(invocation.getTarget() instanceof RoutingStatementHandler){ RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget(); StatementHandler delegate = (StatementHandler) ReflectHelper.getFieldValue(statementHandler, "delegate"); BoundSql boundSql = delegate.getBoundSql(); Object obj = boundSql.getParameterObject(); if (obj instanceof Page<?>) { Page<?> page = (Page<?>) obj; //通過反射獲取delegate父類BaseStatementHandler的mappedStatement屬性 MappedStatement mappedStatement = (MappedStatement)ReflectHelper.getFieldValue(delegate, "mappedStatement"); //攔截到的prepare方法參數(shù)是一個(gè)Connection對(duì)象 Connection connection = (Connection)invocation.getArgs()[0]; //獲取當(dāng)前要執(zhí)行的Sql語句,也就是我們直接在Mapper映射語句中寫的Sql語句 String sql = boundSql.getSql(); //給當(dāng)前的page參數(shù)對(duì)象設(shè)置總記錄數(shù) this.setTotalRecord(page, mappedStatement, connection); //獲取分頁Sql語句 String pageSql = this.getPageSql(page, sql); //利用反射設(shè)置當(dāng)前BoundSql對(duì)應(yīng)的sql屬性為我們建立好的分頁Sql語句 ReflectHelper.setFieldValue(boundSql, "sql", pageSql); } } return invocation.proceed(); } /** * 給當(dāng)前的參數(shù)對(duì)象page設(shè)置總記錄數(shù) * * @param page Mapper映射語句對(duì)應(yīng)的參數(shù)對(duì)象 * @param mappedStatement Mapper映射語句 * @param connection 當(dāng)前的數(shù)據(jù)庫連接 */ private void setTotalRecord(Page<?> page, MappedStatement mappedStatement, Connection connection) { //獲取對(duì)應(yīng)的BoundSql,這個(gè)BoundSql其實(shí)跟我們利用StatementHandler獲取到的BoundSql是同一個(gè)對(duì)象。 //delegate里面的boundSql也是通過mappedStatement.getBoundSql(paramObj)方法獲取到的。 BoundSql boundSql = mappedStatement.getBoundSql(page); //獲取到我們自己寫在Mapper映射語句中對(duì)應(yīng)的Sql語句 String sql = boundSql.getSql(); //通過查詢Sql語句獲取到對(duì)應(yīng)的計(jì)算總記錄數(shù)的sql語句 String countSql = this.getCountSql(sql); //通過BoundSql獲取對(duì)應(yīng)的參數(shù)映射 ListparameterMappings = boundSql.getParameterMappings(); //利用Configuration、查詢記錄數(shù)的Sql語句countSql、參數(shù)映射關(guān)系parameterMappings和參數(shù)對(duì)象page建立查詢記錄數(shù)對(duì)應(yīng)的BoundSql對(duì)象。 BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(), countSql, parameterMappings, page); //通過mappedStatement、參數(shù)對(duì)象page和BoundSql對(duì)象countBoundSql建立一個(gè)用于設(shè)定參數(shù)的ParameterHandler對(duì)象 ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, page, countBoundSql); //通過connection建立一個(gè)countSql對(duì)應(yīng)的PreparedStatement對(duì)象。 PreparedStatement pstmt = null; ResultSet rs = null; try { pstmt = connection.prepareStatement(countSql); //通過parameterHandler給PreparedStatement對(duì)象設(shè)置參數(shù) parameterHandler.setParameters(pstmt); //之后就是執(zhí)行獲取總記錄數(shù)的Sql語句和獲取結(jié)果了。 rs = pstmt.executeQuery(); if (rs.next()) { int totalRecord = rs.getInt(1); //給當(dāng)前的參數(shù)page對(duì)象設(shè)置總記錄數(shù) page.setTotalRecord(totalRecord); } } catch (SQLException e) { e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (pstmt != null) pstmt.close(); } catch (SQLException e) { e.printStackTrace(); } } } /** * 根據(jù)原Sql語句獲取對(duì)應(yīng)的查詢總記錄數(shù)的Sql語句 * @param sql * @return */ private String getCountSql(String sql) { int index = sql.indexOf("from"); return "select count(*) " + sql.substring(index); } /** * 根據(jù)page對(duì)象獲取對(duì)應(yīng)的分頁查詢Sql語句,這里只做了兩種數(shù)據(jù)庫類型,MySQL和Oracle * 其它的數(shù)據(jù)庫都 沒有進(jìn)行分頁 * * @param page 分頁對(duì)象 * @param sql 原sql語句 * @return */ private String getPageSql(Page<?> page, String sql) { StringBuffer sqlBuffer = new StringBuffer(sql); if ("mysql".equalsIgnoreCase(dialect)) { return getMysqlPageSql(page, sqlBuffer); } else if ("oracle".equalsIgnoreCase(dialect)) { return getOraclePageSql(page, sqlBuffer); } return sqlBuffer.toString(); } /** * 獲取Mysql數(shù)據(jù)庫的分頁查詢語句 * @param page 分頁對(duì)象 * @param sqlBuffer 包含原sql語句的StringBuffer對(duì)象 * @return Mysql數(shù)據(jù)庫分頁語句 */ private String getMysqlPageSql(Page<?> page, StringBuffer sqlBuffer) { //計(jì)算第一條記錄的位置,Mysql中記錄的位置是從0開始的。 // System.out.println("page:"+page.getPage()+"-------"+page.getRows()); int offset = (page.getPage() - 1) * page.getRows(); sqlBuffer.append(" limit ").append(offset).append(",").append(page.getRows()); return sqlBuffer.toString(); } /** * 獲取Oracle數(shù)據(jù)庫的分頁查詢語句 * @param page 分頁對(duì)象 * @param sqlBuffer 包含原sql語句的StringBuffer對(duì)象 * @return Oracle數(shù)據(jù)庫的分頁查詢語句 */ private String getOraclePageSql(Page<?> page, StringBuffer sqlBuffer) { //計(jì)算第一條記錄的位置,Oracle分頁是通過rownum進(jìn)行的,而rownum是從1開始的 int offset = (page.getPage() - 1) * page.getRows() + 1; sqlBuffer.insert(0, "select u.*, rownum r from (").append(") u where rownum < ").append(offset + page.getRows()); sqlBuffer.insert(0, "select * from (").append(") where r >= ").append(offset); //上面的Sql語句拼接之后大概是這個(gè)樣子: //select * from (select u.*, rownum r from (select * from t_user) u where rownum < 31) where r >= 16 return sqlBuffer.toString(); } /** * 攔截器對(duì)應(yīng)的封裝原始對(duì)象的方法 */ public Object plugin(Object arg0) { // TODO Auto-generated method stub if (arg0 instanceof StatementHandler) { return Plugin.wrap(arg0, this); } else { return arg0; } } /** * 設(shè)置注冊(cè)攔截器時(shí)設(shè)定的屬性 */ public void setProperties(Properties p) { } public String getDialect() { return dialect; } public void setDialect(String dialect) { this.dialect = dialect; } public String getPageSqlId() { return pageSqlId; } public void setPageSqlId(String pageSqlId) { this.pageSqlId = pageSqlId; } }
xml配置:
Page類
package com.yidao.utils; /**自己看看,需要什么字段加什么字段吧*/ public class Page { private Integer rows; private Integer page = 1; private Integer totalRecord; public Integer getRows() { return rows; } public void setRows(Integer rows) { this.rows = rows; } public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } public Integer getTotalRecord() { return totalRecord; } public void setTotalRecord(Integer totalRecord) { this.totalRecord = totalRecord; } }
ReflectHelper類
package com.yidao.utils; import java.lang.reflect.Field; import org.apache.commons.lang3.reflect.FieldUtils; public class ReflectHelper { public static Object getFieldValue(Object obj , String fieldName ){ if(obj == null){ return null ; } Field targetField = getTargetField(obj.getClass(), fieldName); try { return FieldUtils.readField(targetField, obj, true ) ; } catch (IllegalAccessException e) { e.printStackTrace(); } return null ; } public static Field getTargetField(Class<?> targetClass, String fieldName) { Field field = null; try { if (targetClass == null) { return field; } if (Object.class.equals(targetClass)) { return field; } field = FieldUtils.getDeclaredField(targetClass, fieldName, true); if (field == null) { field = getTargetField(targetClass.getSuperclass(), fieldName); } } catch (Exception e) { } return field; } public static void setFieldValue(Object obj , String fieldName , Object value ){ if(null == obj){return;} Field targetField = getTargetField(obj.getClass(), fieldName); try { FieldUtils.writeField(targetField, obj, value) ; } catch (IllegalAccessException e) { e.printStackTrace(); } } }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。