歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

[Java][MyBatis]物理分頁實現

Mybatis3.0出來已有段時間了,其實自己挺喜歡這樣的一個持久化框架的,因為它簡單實用,學習成本低。Mybatis3.0在整體結構上和ibatis2.X差不多,改進特性如下:

1.解析xml引進了Xpath,不像ibatis2.x那樣業余

2.動態sql用OGNL解析

3.加入注解配置sql,感覺沒什麼特別大的用途,我更喜歡xml方式,代碼和配置分離,這也是ibatis的初衷

4.加強了緩存這塊的功能。Mybatis3.0把緩存模塊分得更細,分為“持久實現(prepetual)”和“資源回收策略實現(eviction)”,更好的對緩存功能進行自己組合和擴展

5.終於加入的plugin功能,就像struts一樣,這樣就可以很好的擴展內部的Executor,,StatementHandler….等內部對象功能。

MyBatis插入時候獲取自增主鍵方法 http://www.linuxidc.com/Linux/2014-03/98965.htm

MyBatis的分頁功能還是基於內存分頁(查找出所有記錄再取出偏移量的記錄,如果jdbc驅支持absolute定位或者rs.next()到指定偏移位置),其實這樣的分頁實現基本沒用,特別是大量數據情況下。不過我們可以通過plugin功能來擴展MyBatis的分頁功能、實現物理分頁。具體做法如下:

1、編寫分頁插件類:

/**
 * 版權所有:華信軟件
 * 項目名稱:ACWS框架類
 * 創建者: Wangdf
 * 創建日期: 2014-4-2
 * 文件說明: ACWS框架分頁接口類
 */
package framework.core.interceptor;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.MapUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import framework.core.util.DBUtil;

/**
 * ACWS框架分頁接口類
 * @author Wangdf
 *
 */
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

 @Override
 public Object intercept(Invocation invocation) throws Throwable {
  StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
  MetaObject metaStatementHandler =  MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
                DEFAULT_OBJECT_WRAPPER_FACTORY);

  RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
  if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
   return invocation.proceed();
  }
 
        // 分離代理對象鏈(由於目標類可能被多個攔截器攔截,從而形成多次代理,通過下面的兩次循環可以分離出最原始的的目標類)
        while (metaStatementHandler.hasGetter("h")) {
            Object object = metaStatementHandler.getValue("h");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
        }
        // 分離最後一個代理對象的目標類
        while (metaStatementHandler.hasGetter("target")) {
            Object object = metaStatementHandler.getValue("target");
            metaStatementHandler = MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);
        }

        BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
        Object params = boundSql.getParameterObject();
        if(params instanceof Map){
         @SuppressWarnings("unchecked")
   Map<String, Object> paramsMap = (Map<String, Object>)params;
         MapUtils.safeAddToMap(paramsMap, "STARTROW", String.valueOf(rowBounds.getOffset()));
         MapUtils.safeAddToMap(paramsMap, "ENDROW", String.valueOf(rowBounds.getLimit()));
        }
       
     String sql = boundSql.getSql();
  StringBuffer sbSql = new StringBuffer();

     // 重寫sql
  Configuration configuration = (Configuration)metaStatementHandler.getValue("delegate.configuration");
  DBUtil dbUtil = new DBUtil(configuration);
        if(dbUtil.isMySQL()){
         sbSql.append(sql).append(" LIMIT ").append(rowBounds.getOffset()-1).append(", ").append(rowBounds.getLimit()-1);
            metaStatementHandler.setValue("delegate.boundSql.sql", sbSql.toString());
           
            // 采用物理分頁後,就不需要mybatis的內存分頁了,所以重置下面的兩個參數
            metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
            metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
  } else if(dbUtil.isOracle()){
   sbSql.append("SELECT * ");
   sbSql.append("  FROM (SELECT ROWNUM RN, NOPAGESQL.*  ");
   sbSql.append("          FROM (").append(sql).append(") NOPAGESQL ");
   sbSql.append("        WHERE ROWNUM <= ").append(rowBounds.getLimit()).append(")");
   sbSql.append(" WHERE RN >= ").append(rowBounds.getOffset());
        metaStatementHandler.setValue("delegate.boundSql.sql", sbSql.toString());
       
        // 采用物理分頁後,就不需要mybatis的內存分頁了,所以重置下面的兩個參數
        metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
        metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
  } else {
   
  }
       
        // 將執行權交給下一個攔截器
        return invocation.proceed();
 }

 @Override
 public Object plugin(Object target) {
        // 當目標類是StatementHandler類型時,才包裝目標類,否者直接返回目標本身,減少目標被代理的次數
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
 }

 @Override
 public void setProperties(Properties properties) {
  // TODO Auto-generated method stub
 
 }

}

2、配置讀取類

/**
 * 版權所有:華信軟件
 * 項目名稱:ACWS框架類
 * 創建者: Wangdf
 * 創建日期: 2014-4-2
 * 文件說明: ACWS框架數據庫相關工具類
 */
package framework.core.util;

import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * ACWS框架數據庫相關工具類
 * @author Wangdf
 */
public class DBUtil {
 private static final String DBTYPE_MYSQL = "MySQL";//支持的類型:MySQL,Oracle
 private static final String DBTYPE_ORACLE = "Oracle";//支持的類型:MySQL,Oracle

 private static Logger logger = LoggerFactory.getLogger(DBUtil.class);
 private Configuration configuration = null;
 
 private String dbType = "";
 private String defaultDateFormat = "";
 
 public DBUtil(Configuration configuration){
  if(configuration == null){
   logger.error("系統啟動失敗:MyBatis Configuration 對象為空!");
   throw new IllegalArgumentException("系統啟動失敗:MyBatis Configuration 對象為空!");
  }
  this.configuration = configuration;
  this.dbType = this.configuration.getVariables().getProperty("dbtype");
    if(StringUtils.isBlank(dbType)){
     logger.error("數據庫類型沒有配置!");
    } else {
     logger.info("數據庫類型為:"+dbType);
    }
  this.defaultDateFormat = this.configuration.getVariables().getProperty("defaultDateFormat");
    if(StringUtils.isBlank(this.defaultDateFormat)){
     this.defaultDateFormat="yyyy-MM-dd";
     logger.info("數據庫日期默認格式字符串沒有指定!系統默認為:yyyy-MM-dd");
    } else {
     logger.info("數據庫日期默認格式字符串:"+this.defaultDateFormat);
    }
 }
 
 /**
  * 判斷是否是Oracle數據庫
  * @return
  * @author wangdf
  */
 public boolean isOracle(){
  return DBTYPE_ORACLE.equals(this.dbType);
 }
 
 
 /**
  * 判斷是否是MySQL數據庫
  * @return
  * @author wangdf
  */
 public boolean isMySQL(){
  return DBTYPE_MYSQL.equals(this.dbType);
 }
 
 /**
  * 取得數據庫類型
  * @return
  * @author wangdf
  */
 public String getDbType(){
  return this.dbType;
 }
 
 
 /**
  * 取得默認日期格式
  * @return
  * @author wangdf
  */
 public String getDefaultDateFormat(){
  return this.defaultDateFormat;
 }

}

3、在mybatis全局配置文件設置

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
 
 <properties>
    <property name="dbtype" value="MySQL"/><!-- 數據庫類型:MySQL、Oracle -->
    <property name="defaultDateFormat" value="yyyy-MM-dd"/>
 </properties>

 <settings>
  <setting name="cacheEnabled" value="true" />
  <setting name="lazyLoadingEnabled" value="true" />
  <setting name="multipleResultSetsEnabled" value="true" />
  <setting name="useColumnLabel" value="true" />
  <setting name="defaultExecutorType" value="REUSE" />
  <setting name="defaultStatementTimeout" value="25000" />
 </settings>
 
 <plugins>
  <plugin interceptor="framework.core.interceptor.PaginationInterceptor"></plugin>
 </plugins>
</configuration>

Copyright © Linux教程網 All Rights Reserved