您的位置:首页 > 编程语言 > Java开发

[Java][MyBatis]物理分页实现

2015-12-31 15:36 295 查看
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>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: