IBATIS物理分頁的實現

 公司的大部分項目都開始使用IBatis作爲O/R Mapping了,但是在使用的過程中也發現了很多不方便和存在爭議的地方,其中一個不方便的地方就是分頁,目前的處理方式都是在sqlMap中寫針對特定數據庫的物理分頁Sql語句,對於oracle數據庫都是在分頁的sql語句裏面加上三層嵌套的sql語句,想了很多辦法,都沒能很好的避免這個問題,無意間在javaeye上看到了《使ibatis支持hibernate式的物理分頁》這篇文章,可點進去已經被刪除了,幸好google了一下有很多人已經收藏了,這裏轉載一下,以便再找不到了.

 

轉載地址:http://www.blogjava.net/libin2722/articles/192504.html

 

一直以來ibatis的分頁都是通過滾動ResultSet實現的,應該算是邏輯分頁吧。邏輯分頁雖然能很乾淨地獨立於特定數據庫,但效率在多數情況下不及特定數據庫支持的物理分頁,而hibernate的分頁則是直接組裝sql,充分利用了特定數據庫的分頁機制,效率相對較高。本文講述的就是如何在不重新編譯ibatis源碼的前提下,爲ibatis引入hibernate式的物理分頁機制。

基本思路就是找到ibatis執行sql的地方,截獲sql並重新組裝sql。通過分析ibatis源碼知道,最終負責執行sql的類是 com.ibatis.sqlmap.engine.execution.SqlExecutor,此類沒有實現任何接口,這多少有點遺憾,因爲接口是相對穩定契約,非大的版本更新,接口一般是不會變的,而類就相對易變一些,所以這裏的代碼只能保證對當前版本(2.1.7)的ibatis有效。下面是 SqlExecutor執行查詢的方法:

Java代碼
/** 
   * Long form of the method to execute a query 
   * 
   * @param request - the request scope 
   * @param conn - the database connection 
   * @param sql - the SQL statement to execute 
   * @param parameters - the parameters for the statement 
   * @param skipResults - the number of results to skip 
   * @param maxResults - the maximum number of results to return 
   * @param callback - the row handler for the query 
   * 
   * @throws SQLException - if the query fails 
   */ 
  public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters,  
                           int skipResults, int maxResults, RowHandlerCallback callback)  
      throws SQLException {  
    ErrorContext errorContext = request.getErrorContext();  
    errorContext.setActivity("executing query");  
    errorContext.setObjectId(sql);  
 
    PreparedStatement ps = null;  
    ResultSet rs = null;  
 
    try {  
      errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");  
 
      Integer rsType = request.getStatement().getResultSetType();  
      if (rsType != null) {  
        ps = conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY);  
      } else {  
        ps = conn.prepareStatement(sql);  
      }  
 
      Integer fetchSize = request.getStatement().getFetchSize();  
      if (fetchSize != null) {  
        ps.setFetchSize(fetchSize.intValue());  
      }  
 
      errorContext.setMoreInfo("Check the parameters (set parameters failed).");  
      request.getParameterMap().setParameters(request, ps, parameters);  
 
      errorContext.setMoreInfo("Check the statement (query failed).");  
 
      ps.execute();  
      rs = getFirstResultSet(ps);  
 
      if (rs != null) {  
        errorContext.setMoreInfo("Check the results (failed to retrieve results).");  
        handleResults(request, rs, skipResults, maxResults, callback);  
      }  
 
      // clear out remaining results  
      while (ps.getMoreResults());  
 
    } finally {  
      try {  
        closeResultSet(rs);  
      } finally {  
        closeStatement(ps);  
      }  
    }  
 
  } 

/**
   * Long form of the method to execute a query
   *
   * @param request - the request scope
   * @param conn - the database connection
   * @param sql - the SQL statement to execute
   * @param parameters - the parameters for the statement
   * @param skipResults - the number of results to skip
   * @param maxResults - the maximum number of results to return
   * @param callback - the row handler for the query
   *
   * @throws SQLException - if the query fails
   */
  public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters,
                           int skipResults, int maxResults, RowHandlerCallback callback)
      throws SQLException {
    ErrorContext errorContext = request.getErrorContext();
    errorContext.setActivity("executing query");
    errorContext.setObjectId(sql);

    PreparedStatement ps = null;
    ResultSet rs = null;

    try {
      errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");

      Integer rsType = request.getStatement().getResultSetType();
      if (rsType != null) {
        ps = conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY);
      } else {
        ps = conn.prepareStatement(sql);
      }

      Integer fetchSize = request.getStatement().getFetchSize();
      if (fetchSize != null) {
        ps.setFetchSize(fetchSize.intValue());
      }

      errorContext.setMoreInfo("Check the parameters (set parameters failed).");
      request.getParameterMap().setParameters(request, ps, parameters);

      errorContext.setMoreInfo("Check the statement (query failed).");

      ps.execute();
      rs = getFirstResultSet(ps);

      if (rs != null) {
        errorContext.setMoreInfo("Check the results (failed to retrieve results).");
        handleResults(request, rs, skipResults, maxResults, callback);
      }

      // clear out remaining results
      while (ps.getMoreResults());

    } finally {
      try {
        closeResultSet(rs);
      } finally {
        closeStatement(ps);
      }
    }

  }

其中handleResults(request, rs, skipResults, maxResults, callback)一句用於處理分頁,其實此時查詢已經執行完畢,可以不必關心handleResults方法,但爲清楚起見,下面來看看 handleResults的實現:

Java代碼
private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {  
    try {  
      request.setResultSet(rs);  
      ResultMap resultMap = request.getResultMap();  
      if (resultMap != null) {  
        // Skip Results  
        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {  
          if (skipResults > 0) {  
            rs.absolute(skipResults);  
          }  
        } else {  
          for (int i = 0; i < skipResults; i++) {  
            if (!rs.next()) {  
              break;  
            }  
          }  
        }  
 
        // Get Results  
        int resultsFetched = 0;  
        while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {  
          Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);  
          callback.handleResultObject(request, columnValues, rs);  
          resultsFetched++;  
        }  
      }  
    } finally {  
      request.setResultSet(null);  
    }  
  } 

private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
    try {
      request.setResultSet(rs);
      ResultMap resultMap = request.getResultMap();
      if (resultMap != null) {
        // Skip Results
        if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
          if (skipResults > 0) {
            rs.absolute(skipResults);
          }
        } else {
          for (int i = 0; i < skipResults; i++) {
            if (!rs.next()) {
              break;
            }
          }
        }

        // Get Results
        int resultsFetched = 0;
        while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
          Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);
          callback.handleResultObject(request, columnValues, rs);
          resultsFetched++;
        }
      }
    } finally {
      request.setResultSet(null);
    }
  }

此處優先使用的是ResultSet的absolute方法定位記錄,是否支持absolute取決於具體數據庫驅動,但一般當前版本的數據庫都支持該方法,如果不支持則逐條跳過前面的記錄。由此可以看出如果數據庫支持absolute,則ibatis內置的分頁策略與特定數據庫的物理分頁效率差距就在於物理分頁查詢與不分頁查詢在數據庫中的執行效率的差距了。因爲查詢執行後讀取數據前數據庫並未把結果全部返回到內存,所以本身在存儲佔用上應該差距不大,如果都使用索引,估計執行速度也差不太多。

繼續我們的話題。其實只要在executeQuery執行前組裝sql,然後將其傳給 executeQuery,並告訴handleResults我們不需要邏輯分頁即可。攔截executeQuery可以採用aop動態實現,也可直接繼承SqlExecutor覆蓋executeQuery來靜態地實現,相比之下後者要簡單許多,而且由於SqlExecutor沒有實現任何接口,比較易變,動態攔截反到增加了維護的工作量,所以我們下面來覆蓋executeQuery:

Java代碼
package com.aladdin.dao.ibatis.ext;  
 
import java.sql.Connection;  
import java.sql.SQLException;  
 
import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  
 
import com.aladdin.dao.dialect.Dialect;  
import com.ibatis.sqlmap.engine.execution.SqlExecutor;  
import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;  
import com.ibatis.sqlmap.engine.scope.RequestScope;  
 
public class LimitSqlExecutor extends SqlExecutor {  
 
    private static final Log logger = LogFactory.getLog(LimitSqlExecutor.class);  
      
    private Dialect dialect;  
 
    private boolean enableLimit = true;  
 
    public Dialect getDialect() {  
        return dialect;  
    }  
 
    public void setDialect(Dialect dialect) {  
        this.dialect = dialect;  
    }  
 
    public boolean isEnableLimit() {  
        return enableLimit;  
    }  
 
    public void setEnableLimit(boolean enableLimit) {  
        this.enableLimit = enableLimit;  
    }  
 
    @Override 
    public void executeQuery(RequestScope request, Connection conn, String sql,  
            Object[] parameters, int skipResults, int maxResults,  
            RowHandlerCallback callback) throws SQLException {  
        if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)  
                && supportsLimit()) {  
            sql = dialect.getLimitString(sql, skipResults, maxResults);  
            if(logger.isDebugEnabled()){  
                logger.debug(sql);  
            }  
            skipResults = NO_SKIPPED_RESULTS;  
            maxResults = NO_MAXIMUM_RESULTS;              
        }  
        super.executeQuery(request, conn, sql, parameters, skipResults,  
                maxResults, callback);  
    }  
 
    public boolean supportsLimit() {  
        if (enableLimit && dialect != null) {  
            return dialect.supportsLimit();  
        }  
        return false;  
    }  
 

package com.aladdin.dao.ibatis.ext;

import java.sql.Connection;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.aladdin.dao.dialect.Dialect;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback;
import com.ibatis.sqlmap.engine.scope.RequestScope;

public class LimitSqlExecutor extends SqlExecutor {

    private static final Log logger = LogFactory.getLog(LimitSqlExecutor.class);
   
    private Dialect dialect;

    private boolean enableLimit = true;

    public Dialect getDialect() {
        return dialect;
    }

    public void setDialect(Dialect dialect) {
        this.dialect = dialect;
    }

    public boolean isEnableLimit() {
        return enableLimit;
    }

    public void setEnableLimit(boolean enableLimit) {
        this.enableLimit = enableLimit;
    }

    @Override
    public void executeQuery(RequestScope request, Connection conn, String sql,
            Object[] parameters, int skipResults, int maxResults,
            RowHandlerCallback callback) throws SQLException {
        if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)
                && supportsLimit()) {
            sql = dialect.getLimitString(sql, skipResults, maxResults);
            if(logger.isDebugEnabled()){
                logger.debug(sql);
            }
            skipResults = NO_SKIPPED_RESULTS;
            maxResults = NO_MAXIMUM_RESULTS;           
        }
        super.executeQuery(request, conn, sql, parameters, skipResults,
                maxResults, callback);
    }

    public boolean supportsLimit() {
        if (enableLimit && dialect != null) {
            return dialect.supportsLimit();
        }
        return false;
    }

} 其中:

Java代碼
skipResults = NO_SKIPPED_RESULTS;  
maxResults = NO_MAXIMUM_RESULTS; 

skipResults = NO_SKIPPED_RESULTS;
maxResults = NO_MAXIMUM_RESULTS; 告訴handleResults不分頁(我們組裝的sql已經使查詢結果是分頁後的結果了),此處引入了類似hibenate中的數據庫方言接口Dialect,其代碼如下:

Java代碼
package com.aladdin.dao.dialect;  
 
public interface Dialect {  
      
    public boolean supportsLimit();  
 
    public String getLimitString(String sql, boolean hasOffset);  
 
    public String getLimitString(String sql, int offset, int limit);  

package com.aladdin.dao.dialect;

public interface Dialect {
   
    public boolean supportsLimit();

    public String getLimitString(String sql, boolean hasOffset);

    public String getLimitString(String sql, int offset, int limit);
}
 下面爲Dialect接口的MySQL實現:

Java代碼
package com.aladdin.dao.dialect;  
 
public class MySQLDialect implements Dialect {  
 
    protected static final String SQL_END_DELIMITER = ";";  
 
    public String getLimitString(String sql, boolean hasOffset) {  
        return new StringBuffer(sql.length() + 20).append(trim(sql)).append(  
                hasOffset ? " limit ?,?" : " limit ?")  
                .append(SQL_END_DELIMITER).toString();  
    }  
 
    public String getLimitString(String sql, int offset, int limit) {  
        sql = trim(sql);  
        StringBuffer sb = new StringBuffer(sql.length() + 20);  
        sb.append(sql);  
        if (offset > 0) {  
            sb.append(" limit ").append(offset).append(',').append(limit)  
                    .append(SQL_END_DELIMITER);  
        } else {  
            sb.append(" limit ").append(limit).append(SQL_END_DELIMITER);  
        }  
        return sb.toString();  
    }  
 
    public boolean supportsLimit() {  
        return true;  
    }  
 
    private String trim(String sql) {  
        sql = sql.trim();  
        if (sql.endsWith(SQL_END_DELIMITER)) {  
            sql = sql.substring(0, sql.length() - 1 
                    - SQL_END_DELIMITER.length());  
        }  
        return sql;  
    }  
 

package com.aladdin.dao.dialect;

public class MySQLDialect implements Dialect {

    protected static final String SQL_END_DELIMITER = ";";

    public String getLimitString(String sql, boolean hasOffset) {
        return new StringBuffer(sql.length() + 20).append(trim(sql)).append(
                hasOffset ? " limit ?,?" : " limit ?")
                .append(SQL_END_DELIMITER).toString();
    }

    public String getLimitString(String sql, int offset, int limit) {
        sql = trim(sql);
        StringBuffer sb = new StringBuffer(sql.length() + 20);
        sb.append(sql);
        if (offset > 0) {
            sb.append(" limit ").append(offset).append(',').append(limit)
                    .append(SQL_END_DELIMITER);
        } else {
            sb.append(" limit ").append(limit).append(SQL_END_DELIMITER);
        }
        return sb.toString();
    }

    public boolean supportsLimit() {
        return true;
    }

    private String trim(String sql) {
        sql = sql.trim();
        if (sql.endsWith(SQL_END_DELIMITER)) {
            sql = sql.substring(0, sql.length() - 1
                    - SQL_END_DELIMITER.length());
        }
        return sql;
    }

} 接下來的工作就是把LimitSqlExecutor注入ibatis中。我們是通過spring來使用ibatis的,所以在我們的dao基類中執行注入,代碼如下:

Java代碼
package com.aladdin.dao.ibatis;  
 
import java.io.Serializable;  
import java.util.List;  
 
import org.springframework.orm.ObjectRetrievalFailureException;  
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;  
 
import com.aladdin.dao.ibatis.ext.LimitSqlExecutor;  
import com.aladdin.domain.BaseObject;  
import com.aladdin.util.ReflectUtil;  
import com.ibatis.sqlmap.client.SqlMapClient;  
import com.ibatis.sqlmap.engine.execution.SqlExecutor;  
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;  
 
public abstract class BaseDaoiBatis extends SqlMapClientDaoSupport {  
 
    private SqlExecutor sqlExecutor;  
 
    public SqlExecutor getSqlExecutor() {  
        return sqlExecutor;  
    }  
 
    public void setSqlExecutor(SqlExecutor sqlExecutor) {  
        this.sqlExecutor = sqlExecutor;  
    }  
 
    public void setEnableLimit(boolean enableLimit) {  
        if (sqlExecutor instanceof LimitSqlExecutor) {  
            ((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);  
        }  
    }  
 
    public void initialize() throws Exception {  
        if (sqlExecutor != null) {  
            SqlMapClient sqlMapClient = getSqlMapClientTemplate()  
                    .getSqlMapClient();  
            if (sqlMapClient instanceof ExtendedSqlMapClient) {  
                ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)  
                        .getDelegate(), "sqlExecutor", SqlExecutor.class,  
                        sqlExecutor);  
            }  
        }  
    }  
 
    ...  
 

package com.aladdin.dao.ibatis;

import java.io.Serializable;
import java.util.List;

import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.aladdin.dao.ibatis.ext.LimitSqlExecutor;
import com.aladdin.domain.BaseObject;
import com.aladdin.util.ReflectUtil;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.execution.SqlExecutor;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;

public abstract class BaseDaoiBatis extends SqlMapClientDaoSupport {

    private SqlExecutor sqlExecutor;

    public SqlExecutor getSqlExecutor() {
        return sqlExecutor;
    }

    public void setSqlExecutor(SqlExecutor sqlExecutor) {
        this.sqlExecutor = sqlExecutor;
    }

    public void setEnableLimit(boolean enableLimit) {
        if (sqlExecutor instanceof LimitSqlExecutor) {
            ((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit);
        }
    }

    public void initialize() throws Exception {
        if (sqlExecutor != null) {
            SqlMapClient sqlMapClient = getSqlMapClientTemplate()
                    .getSqlMapClient();
            if (sqlMapClient instanceof ExtendedSqlMapClient) {
                ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient)
                        .getDelegate(), "sqlExecutor", SqlExecutor.class,
                        sqlExecutor);
            }
        }
    }

    ...

} 其中的initialize方法執行注入,稍後會看到此方法在spring Beans 配置中指定爲init-method。由於sqlExecutor是 com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient的私有成員,且沒有公開的set方法,所以此處通過反射繞過java的訪問控制,下面是ReflectUtil的實現代碼:

Java代碼
package com.aladdin.util;  
 
import java.lang.reflect.Field;  
import java.lang.reflect.Method;  
import java.lang.reflect.Modifier;  
 
import org.apache.commons.logging.Log;  
import org.apache.commons.logging.LogFactory;  
 
public class ReflectUtil {  
 
    private static final Log logger = LogFactory.getLog(ReflectUtil.class);  
 
    public static void setFieldValue(Object target, String fname, Class ftype,  
            Object fvalue) {  
        if (target == null 
                || fname == null 
                || "".equals(fname)  
                || (fvalue != null && !ftype.isAssignableFrom(fvalue.getClass()))) {  
            return;  
        }  
        Class clazz = target.getClass();  
        try {  
            Method method = clazz.getDeclaredMethod("set" 
                    + Character.toUpperCase(fname.charAt(0))  
                    + fname.substring(1), ftype);  
            if (!Modifier.isPublic(method.getModifiers())) {  
                method.setAccessible(true);  
            }  
            method.invoke(target, fvalue);  
 
        } catch (Exception me) {  
            if (logger.isDebugEnabled()) {  
                logger.debug(me);  
            }  
            try {  
                Field field = clazz.getDeclaredField(fname);  
                if (!Modifier.isPublic(field.getModifiers())) {  
                    field.setAccessible(true);  
                }  
                field.set(target, fvalue);  
            } catch (Exception fe) {  
                if (logger.isDebugEnabled()) {  
                    logger.debug(fe);  
                }  
            }  
        }  
    }  

package com.aladdin.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ReflectUtil {

    private static final Log logger = LogFactory.getLog(ReflectUtil.class);

    public static void setFieldValue(Object target, String fname, Class ftype,
            Object fvalue) {
        if (target == null
                || fname == null
                || "".equals(fname)
                || (fvalue != null && !ftype.isAssignableFrom(fvalue.getClass()))) {
            return;
        }
        Class clazz = target.getClass();
        try {
            Method method = clazz.getDeclaredMethod("set"
                    + Character.toUpperCase(fname.charAt(0))
                    + fname.substring(1), ftype);
            if (!Modifier.isPublic(method.getModifiers())) {
                method.setAccessible(true);
            }
            method.invoke(target, fvalue);

        } catch (Exception me) {
            if (logger.isDebugEnabled()) {
                logger.debug(me);
            }
            try {
                Field field = clazz.getDeclaredField(fname);
                if (!Modifier.isPublic(field.getModifiers())) {
                    field.setAccessible(true);
                }
                field.set(target, fvalue);
            } catch (Exception fe) {
                if (logger.isDebugEnabled()) {
                    logger.debug(fe);
                }
            }
        }
    }
}
  到此剩下的就是通過Spring將sqlExecutor注入BaseDaoiBatis中了,下面是Spring Beans配置文件:

Xml代碼
<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  
    "http://www.springframework.org/dtd/spring-beans.dtd"> 
 
<beans> 
    <!-- Transaction manager for a single JDBC DataSource --> 
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
        <property name="dataSource"> 
            <ref bean="dataSource" /> 
        </property> 
    </bean> 
      
    <!-- SqlMap setup for iBATIS Database Layer --> 
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> 
        <property name="configLocation"> 
            <value>classpath:/com/aladdin/dao/ibatis/sql-map-config.xml</value> 
        </property> 
        <property name="dataSource"> 
            <ref bean="dataSource" /> 
        </property> 
    </bean> 
 
    <bean id="sqlExecutor" class="com.aladdin.dao.ibatis.ext.LimitSqlExecutor"> 
        <property name="dialect"> 
            <bean class="com.aladdin.dao.dialect.MySQLDialect" /> 
        </property> 
    </bean> 
      
    <bean id="baseDao" abstract="true" class="com.aladdin.dao.ibatis.BaseDaoiBatis" init-method="initialize"> 
        <property name="dataSource"> 
            <ref bean="dataSource" /> 
        </property> 
        <property name="sqlMapClient"> 
            <ref bean="sqlMapClient" /> 
        </property> 
        <property name="sqlExecutor"> 
            <ref bean="sqlExecutor" /> 
        </property>   
    </bean>   
      
    <bean id="userDao" class="com.aladdin.dao.ibatis.UserDaoiBatis" parent="baseDao" />   
 
    <bean id="roleDao" class="com.aladdin.dao.ibatis.RoleDaoiBatis" parent="baseDao" /> 
      
    <bean id="resourceDao" class="com.aladdin.dao.ibatis.ResourceDaoiBatis" parent="baseDao" /> 
      
</beans> 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <!-- Transaction manager for a single JDBC DataSource -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>
   
    <!-- SqlMap setup for iBATIS Database Layer -->
    <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
        <property name="configLocation">
            <value>classpath:/com/aladdin/dao/ibatis/sql-map-config.xml</value>
        </property>
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
    </bean>

    <bean id="sqlExecutor" class="com.aladdin.dao.ibatis.ext.LimitSqlExecutor">
        <property name="dialect">
            <bean class="com.aladdin.dao.dialect.MySQLDialect" />
        </property>
    </bean>
   
    <bean id="baseDao" abstract="true" class="com.aladdin.dao.ibatis.BaseDaoiBatis" init-method="initialize">
        <property name="dataSource">
            <ref bean="dataSource" />
        </property>
        <property name="sqlMapClient">
            <ref bean="sqlMapClient" />
        </property>
        <property name="sqlExecutor">
            <ref bean="sqlExecutor" />
        </property>
    </bean>
   
    <bean id="userDao" class="com.aladdin.dao.ibatis.UserDaoiBatis" parent="baseDao" />

    <bean id="roleDao" class="com.aladdin.dao.ibatis.RoleDaoiBatis" parent="baseDao" />
   
    <bean id="resourceDao" class="com.aladdin.dao.ibatis.ResourceDaoiBatis" parent="baseDao" />
   
</beans>
 

此後就可以通過調用org.springframework.orm.ibatis.SqlMapClientTemplate的

public List queryForList(final String statementName, final Object parameterObject, final int skipResults, final int maxResults)   throws DataAccessException

public PaginatedList queryForPaginatedList(final String statementName, final Object parameterObject, final int pageSize)   throws DataAccessException

得到分頁結果了。建議使用第一個方法,第二個方法返回的是PaginatedList,雖然使用簡單,但是其獲得指定頁的數據是跨過我們的dao直接訪問ibatis的,不方便統一管理。

發佈了25 篇原創文章 · 獲贊 1 · 訪問量 1804
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章