zoukankan      html  css  js  c++  java
  • [转]修改 ibatis 分页机制(ORACLE物理分页)

      修改 ibatis 分页机制(ORACLE物理分页)

    针对ibatis 分页机制的修改例子网上也有很多,本人也是参考后做的修改.如下:

    1、首先增加配置文件

     

    1. <bean id="sqlExecutor" class="com.ibatis.sqlmap.engine.execution.XSqlExecutor">  
    2.         <property name="dialect">   
    3.             <bean class="com.ibatis.sqlmap.engine.imp.DialectImp" />   
    4.         </property>   
    5.         <property name="enableLimit">     
    6.             <value>true</value>     
    7.         </property>     
    8.     </bean>   

    此类是继承地层分页代码从而实现修改SQL语句

    1. package com.ibatis.sqlmap.engine.execution;   
    2.   
    3.  import java.sql.Connection;    
    4.  import java.sql.SQLException;    
    5.   
    6.  import com.ibatis.sqlmap.engine.inter.Dialect;    
    7.  import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;    
    8.  import com.ibatis.sqlmap.engine.scope.StatementScope;    
    9.   
    10.  /**   
    11.  *   
    12.  * @author chenyanji  
    13.  *   
    14.  * @project Vstsoft newsx code.  
    15.  * @datetime Mar 15, 2009 12:07:18 AM  
    16.  */  
    17.  public class XSqlExecutor extends SqlExecutor{    
    18.   
    19.        
    20.     private Dialect dialect;    
    21.   
    22.     private boolean enableLimit = true;    
    23.   
    24.     public Dialect getDialect() {    
    25.         return dialect;    
    26.     }   
    27.   
    28.     public void setDialect(Dialect dialect) {    
    29.         this.dialect = dialect;    
    30.     }   
    31.   
    32.     public boolean isEnableLimit() {    
    33.         return enableLimit;    
    34.     }   
    35.   
    36.     public void setEnableLimit(boolean enableLimit) {    
    37.         this.enableLimit = enableLimit;    
    38.     }   
    39.   
    40.     /**   
    41.      * 重写SqlExecutor.executeQuery方法 实现ORACLE的SQL物理分页 see OracleDialect  
    42.      */  
    43.     public void executeQuery(StatementScope request, Connection conn,    
    44.             String sql, Object[] parameters, int skipResults, int maxResults,    
    45.             RowHandlerCallback callback) throws SQLException {    
    46.         if (isLimit(sql, skipResults, maxResults)) {// 有分页信息、可物理分页SQL    
    47.             sql = dialect.getOracleLimit(sql, skipResults, maxResults);// 获得物理分页SQL    
    48.             skipResults = NO_SKIPPED_RESULTS;// 设置skipResults为SqlExecutor不分页    
    49.             maxResults = NO_MAXIMUM_RESULTS;// 设置maxResults为SqlExecutor不分页    
    50.         }   
    51.         super.executeQuery(request, conn, sql, parameters, skipResults,   
    52.                 maxResults, callback);// 使用不分页机制调用SqlExecutor查询方法   
    53.     }   
    54.     /**  
    55.      * 是否允许执行分页  
    56.      *   
    57.      * @param sql  
    58.      * @param skipResults  
    59.      * @param maxResults  
    60.      * @return  
    61.      */  
    62.     private boolean isLimit(String sql, int skipResults, int maxResults) {   
    63.         return (skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)   
    64.                 && enableLimit && isSelect(sql);   
    65.     }   
    66.   
    67.     /**  
    68.      * 是否可物理分页SQL  
    69.      *   
    70.      * @param sql  
    71.      * @return  
    72.      */  
    73.     private boolean isSelect(String sql) {   
    74.         if (sql.toLowerCase().indexOf("select") >= 0) {   
    75.             if (sql.toLowerCase().indexOf("rownum") >= 0) {   
    76.                 return false;   
    77.             }   
    78.             return true;   
    79.         }   
    80.         return false;   
    81.     }   
    82.   
    83. }  

    修改语句接口

    1. package com.ibatis.sqlmap.engine.inter;   
    2.   
    3.  /**  
    4.  *   
    5.  * @author chenyanji  
    6.  *   
    7.  * @project Vstsoft newsx code.  
    8.  * @datetime Mar 15, 2009 12:07:24 AM  
    9.  */  
    10.  public interface Dialect {   
    11.   
    12.     /**  
    13.      * 获得分页SQL  
    14.      *   
    15.      * @param sql  
    16.      * @param offset  
    17.      * @param limit  
    18.      * @return  
    19.      */  
    20.     public String getOracleLimit(String sql, int offset, int limit);   
    21.   
    22. }  

    实现类

    1. package com.ibatis.sqlmap.engine.imp;   
    2.   
    3.  import com.ibatis.sqlmap.engine.inter.Dialect;   
    4.   
    5.  /**  
    6.  *   
    7.  * @author chenyanji  
    8.  *   
    9.  * @project Vstsoft newsx code.  
    10.  * @datetime Mar 15, 2009 12:06:18 AM  
    11.  */  
    12.  public class DialectImp implements Dialect {   
    13.   
    14.     protected static final String SQL_END_DELIMITER = ";";   
    15.   
    16.     public String getOracleLimit(String sql, int offset, int limit) {   
    17.         if (offset == 1) {   
    18.             offset = 0;   
    19.         }   
    20.         StringBuffer pageStr = new StringBuffer();   
    21.         pageStr   
    22.                 .append("select * from ( select row_limit.*, rownum rownum_ from (");   
    23.         pageStr.append(this.trim(sql));   
    24.         pageStr.append(" ) row_limit where rownum <= ");   
    25.         pageStr.append(limit + offset);   
    26.         pageStr.append(" ) where rownum_ >");   
    27.         pageStr.append(offset);   
    28.         return pageStr.toString();   
    29.     }   
    30.   
    31.     /**  
    32.      * 去掉当前SQL 后分号  
    33.      *   
    34.      * @param sql  
    35.      * @return  
    36.      */  
    37.     private String trim(String sql) {   
    38.         sql = sql.trim();   
    39.         if (sql.endsWith(SQL_END_DELIMITER)) {   
    40.             sql = sql.substring(0, sql.length() - 1  
    41.                     - SQL_END_DELIMITER.length());   
    42.         }   
    43.         return sql;   
    44.     }   
    45. }  

    2、增加继承类(ibatis调用地层类)

    1. package org.springside.core.dao.support;   
    2.   
    3.  import java.util.TimeZone;   
    4.   
    5.  import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;   
    6.  import org.springside.core.utils.ReflectUtil;   
    7.   
    8.  import com.ibatis.sqlmap.client.SqlMapClient;   
    9.  import com.ibatis.sqlmap.engine.execution.SqlExecutor;   
    10. import com.ibatis.sqlmap.engine.execution.XSqlExecutor;   
    11. import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;   
    12. import com.vstsoft.csi.core.log.Logger;   
    13.   
    14. /**  
    15.  *   
    16.  * @author chenyanji  
    17.  *   
    18.  * @project Vstsoft newsx code.  
    19.  * @datetime Mar 15, 2009 12:07:11 AM  
    20.  */  
    21. public abstract class XSqlMapClientDaoSupport extends SqlMapClientDaoSupport {   
    22.   
    23.     /**  
    24.      * chenyanji.修正JDK时区问题  
    25.      */  
    26.     public XSqlMapClientDaoSupport(){   
    27.         TimeZone.setDefault(TimeZone.getTimeZone("ETC/GMT-8"));    
    28.     }   
    29. //  protected  Logger logger = new Logger(this.getClass());   
    30.     private SqlExecutor sqlExecutor;   
    31.   
    32.     public SqlExecutor getSqlExecutor() {   
    33.         return sqlExecutor;   
    34.     }   
    35.   
    36.     public void setSqlExecutor(SqlExecutor sqlExecutor) {   
    37.         this.sqlExecutor = sqlExecutor;   
    38.     }   
    39.   
    40.     public void setEnableLimit(boolean enableLimit) {   
    41.         if (sqlExecutor instanceof XSqlExecutor) {   
    42.             ((XSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);   
    43.         }   
    44.     }   
    45.   
    46.     /**  
    47.      * 将封装过的sqlExecutor对象SET给getSqlMapClientTemplate,从而改变继承sqlExecutor对象。  
    48.      *   
    49.      * @throws Exception  
    50.      */  
    51.     @SuppressWarnings("deprecation")   
    52.     public void initialized() {   
    53.         if (sqlExecutor != null) {   
    54.             SqlMapClient sqlMapClient = getSqlMapClientTemplate()   
    55.                     .getSqlMapClient();   
    56.             if (sqlMapClient instanceof ExtendedSqlMapClient) {   
    57.                 ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)   
    58.                         .getDelegate(), "sqlExecutor", SqlExecutor.class,   
    59.                         sqlExecutor);   
    60. //              logger.info(((ExtendedSqlMapClient) sqlMapClient).getDelegate()   
    61. //                      .getClass().getSimpleName()   
    62. //                      + "'s OracleLimit initialized.");   
    63.             } else {   
    64. //              logger.info("null OracleLimit initialized.");   
    65.             }   
    66.         }   
    67.     }   
    68. }  

    本人使用springside作为执行数据库地层类,所以直接修改IBatisGenericDao继承为XSqlMapClientDaoSupport就可以

    1. public class IBatisGenericDao extends XSqlMapClientDaoSupport {  

     3、为所有SPRING配置文件增加初始化方法,作用是修改IBATIS地层SqlMapClientDaoSupport的实现类,使用我们自己写的类,以便方便控制于修改SQL

    1. <beans  default-init-method="initialized">  
    1. package org.springside.core.utils;   
    2.   
    3. import java.lang.reflect.Field;   
    4. import java.lang.reflect.Method;   
    5. import java.lang.reflect.Modifier;   
    6.   
    7. /**  
    8.  *   
    9.  * @author chenyanji  
    10.  *   
    11.  * @project Vstsoft newsx code.  
    12.  * @datetime Mar 15, 2009 12:07:33 AM  
    13.  */  
    14. public class ReflectUtil {   
    15.   
    16.     /**  
    17.      * 通过反射绕过java的访问控制,向对象中SET属性。  
    18.      * @param target  
    19.      * @param fname  
    20.      * @param ftype  
    21.      * @param fvalue  
    22.      */  
    23.     public static void setFieldValue(Object target, String fname, Class ftype, Object fvalue) {   
    24.         if (target == null || fname == null || "".equals(fname)   
    25.                 || (fvalue != null && !ftype.isAssignableFrom(fvalue.getClass()))) {   
    26.             return;   
    27.         }   
    28.         Class clazz = target.getClass();   
    29.         try {   
    30.             Method method = clazz.getDeclaredMethod("set" + Character.toUpperCase(fname.charAt(0))   
    31.                     + fname.substring(1), ftype);   
    32.             if (!Modifier.isPublic(method.getModifiers())) {   
    33.                 method.setAccessible(true);   
    34.             }   
    35.             method.invoke(target, fvalue);   
    36.   
    37.         } catch (Exception me) {   
    38.             try {   
    39.                 Field field = clazz.getDeclaredField(fname);   
    40.                 if (!Modifier.isPublic(field.getModifiers())) {   
    41.                     field.setAccessible(true);   
    42.                 }   
    43.                 field.set(target, fvalue);   
    44.             } catch (Exception fe) {   
    45.             }   
    46.         }   
    47.     }   
    48. }  

    到此步,已经完成分页修改,和网上说的改法基本一致。

    原理,初始化自己BEAN的同时也会初始化到父类父类。。。默认初始化方法initialized来进行修改IBATIS

    经测试出现问题:已知一未初始化SQLMAP,使用分页进行初始化(SQLMAP会多映射一列,ORACLE3层分页导致),在进行不分页查询会出现少列错误,也就是说SQLMAP 多了一个映射列,查询语句无法进行完整匹配。SQLMAP唯一不足的是:SQLMAP映射可以不查询出的列少,但是绝对不允许多,现在我们使用分页初始化就多出一列。本人的解决办法是直接修改JAR包(未找到好的解决办法)


    com.ibatis.sqlmap.engine.mapping.result.ResultMap 中 getPrimitiveResultMappingValue方法进行修改

    增加过滤条件判断
     if(columnName.equals("ROWNUM_"))
                    value = "";

    (直接哪JAVA文件进行修改编译,然后直接替换包中CLASS,如需要可以所要)

    最后提一点,也是容易忽略的地方,加了ORACLE分页后,原始SQL不能出现重复列、rownum,否则提示无法映射列错误!如 select * from tablea as a, tableb as b  where a.id=b.id 这样语句加上分页就会出现ID无法映射,两表都包含ID外层分页SQL无法解析ID是从那个表来的。

     因冲忙没有做详细讲解,如果质疑可以进一步讨论

    本人最终实现完全修改JAR包,如果需要JAR包,请加入群(共享)本包只针对ORACLE进行修改,无其他扩展。

  • 相关阅读:
    Java RandomAccessFile与MappedByteBuffer
    Apache httpClient
    gitolite migration to bitbucket
    logback身份证脱敏
    身份证号码-正则表达式
    webservice-整理
    Java高编译低运行错误(ConcurrentHashMap.keySet)
    IIS(互联网信息服务)
    ASP.NET MVC:UrlHelper.cs
    TortoiseSVN 和 VisualSVN
  • 原文地址:https://www.cnblogs.com/huqingyu/p/1705483.html
Copyright © 2011-2022 走看看