1. 程式人生 > >springMVC、myBatis的物理分頁和高階查詢

springMVC、myBatis的物理分頁和高階查詢

最新專案用到springMVC和mybatis,分頁其實用一個RowBounds可以實現,但是高階查詢不好封裝, 經過反覆測試,總算搞出來了,感覺封裝的不是很好,有待優化和提高!

原理:利用mybatis自定義外掛功能,自定義一個攔截器,攔截需要分頁的sql,並想辦法通過BoundSql物件進行處理,大致分8步:

1、獲得BoundSql物件

2、獲取原始的寫在配置檔案中的SQL

3、攔截到mapper中定義的執行查詢方法中的引數

4、解析引數,獲取高階查詢引數資訊

5、解析引數,獲取查詢限制條件

6、根據4、5中的引數拼裝並重新生成SQL語句

7、將SQL設定回BoundSql物件中

8、完成。

攔截器:

Java程式碼  收藏程式碼
  1. package com.wtas.page.interceptor;  
  2. import java.sql.Connection;  
  3. import java.sql.PreparedStatement;  
  4. import java.sql.ResultSet;  
  5. import java.sql.SQLException;  
  6. import java.util.List;  
  7. import java.util.Map;  
  8. import java.util.Properties;  
  9. import java.util.Set;  
  10. import javax.xml.bind.PropertyException;  
  11. import org.apache.ibatis.executor.ErrorContext;  
  12. import org.apache.ibatis.executor.ExecutorException;  
  13. import org.apache.ibatis.executor.statement.BaseStatementHandler;  
  14. import org.apache.ibatis.executor.statement.RoutingStatementHandler;  
  15. import org.apache.ibatis.executor.statement.StatementHandler;  
  16. import org.apache.ibatis.mapping.BoundSql;  
  17. import org.apache.ibatis.mapping.MappedStatement;  
  18. import org.apache.ibatis.mapping.ParameterMapping;  
  19. import org.apache.ibatis.mapping.ParameterMode;  
  20. import org.apache.ibatis.plugin.Interceptor;  
  21. import org.apache.ibatis.plugin.Intercepts;  
  22. import org.apache.ibatis.plugin.Invocation;  
  23. import org.apache.ibatis.plugin.Plugin;  
  24. import org.apache.ibatis.plugin.Signature;  
  25. import org.apache.ibatis.reflection.MetaObject;  
  26. import org.apache.ibatis.reflection.property.PropertyTokenizer;  
  27. import org.apache.ibatis.scripting.xmltags.ForEachSqlNode;  
  28. import org.apache.ibatis.session.Configuration;  
  29. import org.apache.ibatis.type.TypeHandler;  
  30. import org.apache.ibatis.type.TypeHandlerRegistry;  
  31. import org.slf4j.Logger;  
  32. import org.slf4j.LoggerFactory;  
  33. import com.wtas.page.PageContext;  
  34. import com.wtas.page.Pager;  
  35. import com.wtas.page.Query;  
  36. import com.wtas.utils.SystemUtil;  
  37. /** 
  38.  * 查詢分頁攔截器,使用者攔截SQL,並加上分頁的引數和高階查詢條件 
  39.  *  
  40.  * @author dendy 
  41.  *  
  42.  */  
  43. @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })  
  44. public class PaginationInterceptor implements Interceptor {  
  45.     private final Logger logger = LoggerFactory  
  46.             .getLogger(PaginationInterceptor.class);  
  47.     private String dialect = "";  
  48.     // 暫時不需要這個引數,現在根據引數型別來判斷是否是分頁sql  
  49.     // private String pageMethodPattern = "";  
  50.     public Object intercept(Invocation ivk) throws Throwable {  
  51.         if (!(ivk.getTarget() instanceof RoutingStatementHandler)) {  
  52.             return ivk.proceed();  
  53.         }  
  54.         RoutingStatementHandler statementHandler = (RoutingStatementHandler) ivk  
  55.                 .getTarget();  
  56.         BaseStatementHandler delegate = (BaseStatementHandler) SystemUtil  
  57.                 .getValueByFieldName(statementHandler, "delegate");  
  58.         MappedStatement mappedStatement = (MappedStatement) SystemUtil  
  59.                 .getValueByFieldName(delegate, "mappedStatement");  
  60.         // BoundSql封裝了sql語句  
  61.         BoundSql boundSql = delegate.getBoundSql();  
  62.         // 獲得查詢物件  
  63.         Object parameterObject = boundSql.getParameterObject();  
  64.         // 根據引數型別判斷是否是分頁方法  
  65.         if (!(parameterObject instanceof Query)) {  
  66.             return ivk.proceed();  
  67.         }  
  68.         logger.debug(" beginning to intercept page SQL...");  
  69.         Connection connection = (Connection) ivk.getArgs()[0];  
  70.         String sql = boundSql.getSql();  
  71.         Query query = (Query) parameterObject;  
  72.         // 查詢引數物件  
  73.         Pager pager = null;  
  74.         // 查詢條件Map  
  75.         Map<String, Object> conditions = query.getQueryParams();  
  76.         pager = query.getPager();  
  77.         // 拼裝查詢條件  
  78.         if (conditions != null) {  
  79.             Set<String> keys = conditions.keySet();  
  80.             Object value = null;  
  81.             StringBuffer sb = new StringBuffer();  
  82.             boolean first = true;  
  83.             for (String key : keys) {  
  84.                 value = conditions.get(key);  
  85.                 if (first) {  
  86.                     sb.append(" where ").append(key).append(value);  
  87.                     first = !first;  
  88.                 } else {  
  89.                     sb.append(" and ").append(key).append(value);  
  90.                 }  
  91.             }  
  92.             sql += sb.toString();  
  93.         }  
  94.         // 獲取查詢數來的總數目  
  95.         String countSql = "SELECT COUNT(0) FROM (" + sql + ") AS tmp ";  
  96.         PreparedStatement countStmt = connection.prepareStatement(countSql);  
  97.         BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),  
  98.                 countSql, boundSql.getParameterMappings(), parameterObject);  
  99.         setParameters(countStmt, mappedStatement, countBS, parameterObject);  
  100.         ResultSet rs = countStmt.executeQuery();  
  101.         int count = 0;  
  102.         if (rs.next()) {  
  103.             count = rs.getInt(1);  
  104.         }  
  105.         rs.close();  
  106.         countStmt.close();  
  107.         // 設定總記錄數  
  108.         pager.setTotalResult(count);  
  109.         // 設定總頁數  
  110.         pager.setTotalPage((count + pager.getShowCount() - 1)  
  111.                 / pager.getShowCount());  
  112.         // 放到作用於  
  113.         PageContext.getInstance().set(pager);  
  114.         // 拼裝查詢引數  
  115.         String pageSql = generatePageSql(sql, pager);  
  116.         SystemUtil.setValueByFieldName(boundSql, "sql", pageSql);  
  117.         logger.debug("generated pageSql is : " + pageSql);  
  118.         return ivk.proceed();  
  119.     }  
  120.     /** 
  121.      * setting parameters 
  122.      *  
  123.      * @param ps 
  124.      * @param mappedStatement 
  125.      * @param boundSql 
  126.      * @param parameterObject 
  127.      * @throws SQLException 
  128.      */  
  129.     private void setParameters(PreparedStatement ps,  
  130.             MappedStatement mappedStatement, BoundSql boundSql,  
  131.             Object parameterObject) throws SQLException {  
  132.         ErrorContext.instance().activity("setting parameters")  
  133.                 .object(mappedStatement.getParameterMap().getId());  
  134.         List<ParameterMapping> parameterMappings = boundSql  
  135.                 .getParameterMappings();  
  136.         if (parameterMappings != null) {  
  137.             Configuration configuration = mappedStatement.getConfiguration();  
  138.             TypeHandlerRegistry typeHandlerRegistry = configuration  
  139.                     .getTypeHandlerRegistry();  
  140.             MetaObject metaObject = parameterObject == null ? null  
  141.                     : configuration.newMetaObject(parameterObject);  
  142.             for (int i = 0; i < parameterMappings.size(); i++) {  
  143.                 ParameterMapping parameterMapping = parameterMappings.get(i);  
  144.                 if (parameterMapping.getMode() != ParameterMode.OUT) {  
  145.                     Object value;  
  146.                     String propertyName = parameterMapping.getProperty();  
  147.                     PropertyTokenizer prop = new PropertyTokenizer(propertyName);  
  148.                     if (parameterObject == null) {  
  149.                         value = null;  
  150.                     } else if (typeHandlerRegistry  
  151.                             .hasTypeHandler(parameterObject.getClass())) {  
  152.                         value = parameterObject;  
  153.                     } else if (boundSql.hasAdditionalParameter(propertyName)) {  
  154.                         value = boundSql.getAdditionalParameter(propertyName);  
  155.                     } else if (propertyName  
  156.                             .startsWith(ForEachSqlNode.ITEM_PREFIX)  
  157.                             && boundSql.hasAdditionalParameter(prop.getName())) {  
  158.                         value = boundSql.getAdditionalParameter(prop.getName());  
  159.                         if (value != null) {  
  160.                             value = configuration.newMetaObject(value)  
  161.                                     .getValue(  
  162.                                             propertyName.substring(prop  
  163.                                                     .getName().length()));  
  164.                         }  
  165. <