`
icefire
  • 浏览: 125343 次
  • 性别: Icon_minigender_1
  • 来自: 深圳、重庆
社区版块
存档分类
最新评论

mybatis分页实现1

阅读更多
最近开始尝试用mybatis,比较郁闷其对分页的实现,看了网上一些例子,还是不满意。最好的应该是rapid-framework里的实现了,但没实现分页参数的参数化,参数都是硬编码在sql里,在oracle这样的数据库里,性能影响还是有的。

下面是我的实现,但还是觉得有些复杂。

StatementHandlerInterceptor.java 主要是负责修改sql,在rapid-framework,其实就只有这样一个Interceptor,但如果要设置参数,就不止了。
@Intercepts({
		@Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }),
		@Signature(type = StatementHandler.class, method = "parameterize", args = { Statement.class }) })
public class StatementHandlerInterceptor extends
		AbstractStatementHandlerInterceptor implements Interceptor {
	
	private Object prepare(Invocation invocation) throws Throwable {
		StatementHandler statement = getStatementHandler(invocation);
		
		if (statement instanceof SimpleStatementHandler 
				|| statement instanceof PreparedStatementHandler) {

			RowBounds rowBounds = getRowBounds(statement);
		
			if (hasBounds(rowBounds)) {
				BoundSql boundSql = statement.getBoundSql();
				String sql = boundSql.getSql();
				
				if (statement instanceof SimpleStatementHandler) {
					sql = dialect.getLimitString(sql, rowBounds.getOffset(),
							rowBounds.getLimit());
				}
				else if (statement instanceof PreparedStatementHandler) {
					sql = dialect.getLimitString(sql, rowBounds.getOffset() > 0);
				}
				FieldUtils.setFieldValue(boundSql, "sql", sql);
			}
		}

		return invocation.proceed();
	}
	
	private Object parameterize(Invocation invocation) throws Throwable {
		Statement statement = (Statement) invocation.getArgs()[0];

		Object rtn = invocation.proceed();

		if (statement instanceof PreparedStatement) {
			PreparedStatement ps = (PreparedStatement) statement;

			StatementHandler statementHandler = getStatementHandler(invocation);
			RowBounds rowBounds = getRowBounds(statementHandler);

			if (hasBounds(rowBounds)) {
				BoundSql boundSql = statementHandler.getBoundSql();
				int parameterSize = boundSql.getParameterMappings().size();
				dialect.setLimitParamters(ps, parameterSize,
						rowBounds.getOffset(), rowBounds.getLimit());
			}
		}
		return rtn;
	}

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		Method m = invocation.getMethod();
		if ("prepare".equals(m.getName())) {
			return prepare(invocation);
		}
		else if ("parameterize".equals(m.getName())) {
			return parameterize(invocation);
		}
		return invocation.proceed();
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
	}

}


prepare和parameterize是在两个不同点调用的,一个负责生成Statement,一个负责设置参数。本来是想分开写成两个Interceptor,但后来发现多次针对同一个类进行拦截,是无法获取其内部属性的,Plugin.wrap(target, this)是生成一个代理类,下一个拦截器就是在这个基础上继续做代理。这个和struts2之类的拦截器是不一样的。


好像这样是可以了,但实际还是不够的。因为StatementHandler和FastResultSetHandler是同时生成,而且都接引用了RowBounds参数,在FastResultSetHandler还是分页形式存在,如果不修正,会导致mybatis用游标移动后取数据。

ResultSetHandlerInterceptor.java 负责修正RowBounds
@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class ResultSetHandlerInterceptor implements Interceptor {

	public Object intercept(Invocation invocation) throws Throwable {
		ResultSetHandler resultSet = (ResultSetHandler) invocation.getTarget();

		// 不用浪费性能做属性存在判断
		RowBounds rowBounds = (RowBounds) FieldUtils.getFieldValue(resultSet,
				"rowBounds");

		if (rowBounds.getLimit() > 0
				&& rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT) {
			// 强制不允许游标分页
			FieldUtils.setFieldValue(resultSet, "rowBounds", RowBounds.DEFAULT);
		}
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
	}
}


不过现在想想,用反射直接修改RowBounds里的属性,不就可以不用ResultSetHandlerInterceptor了?哎!又SB了一次。后面改进。

其他辅助类

public abstract class AbstractStatementHandlerInterceptor implements Interceptor, InitializingBean {
	
	private Class<Dialect> dialectClass;

	public void setDialectClass(Class<Dialect> dialectClass) {
		this.dialectClass = dialectClass;
	}
	
	protected Dialect dialect;

	public void setDialect(Dialect dialect) {
		this.dialect = dialect;
	}
	
	public void afterPropertiesSet() throws Exception {
		setDialect(dialectClass.newInstance());
	}

	protected StatementHandler getStatementHandler(Invocation invocation) {
		StatementHandler statement = (StatementHandler) invocation.getTarget();
		if (statement instanceof RoutingStatementHandler) {
			statement = (StatementHandler) FieldUtils.getFieldValue(statement,
					"delegate");
		}
		return statement;
	}
	
	protected RowBounds getRowBounds(StatementHandler statement) {
		return (RowBounds) FieldUtils.getFieldValue(statement, "rowBounds");
	}
	
	protected boolean hasBounds(RowBounds rowBounds) {
		return (rowBounds != null 
				&& rowBounds.getLimit() > 0 
				&& rowBounds.getLimit() < RowBounds.NO_ROW_LIMIT);
	}

}

public interface Dialect {
	
	public void setLimitParamters(PreparedStatement ps, int parameterSize, int offset, int limit) throws SQLException;
	
	public String getLimitString(String sql, boolean hasOffset);

	public String getLimitString(String sql, int offset, int limit);
}

public abstract class FieldUtils {
	
	public static boolean hasField(Object target, String fieldName, Class<?> type) {
		return ReflectionUtils.findField(target.getClass(), fieldName, type) == null;
	}

	public static Object getFieldValue(Object target, String fieldName) {
		Field field = ReflectionUtils.findField(target.getClass(), fieldName);
		ReflectionUtils.makeAccessible(field);
		return ReflectionUtils.getField(field, target);
	}
	
	public static void setFieldValue(Object target, String fieldName, Object value) {
		Field field = ReflectionUtils.findField(target.getClass(), fieldName);
		ReflectionUtils.makeAccessible(field);
		ReflectionUtils.setField(field, target, value);
	}
}

ReflectionUtils是spring里的。

spring 集成用到的。增加interceptors集合,直接通过spring注入interceptor.
public class MyBatisSessionFactoryBean extends SqlSessionFactoryBean {
	
	private List<Interceptor> interceptors = Collections.emptyList();

	public void setInterceptors(List<Interceptor> interceptors) {
		this.interceptors = interceptors;
	}

	protected SqlSessionFactory buildSqlSessionFactory() throws IOException,
			IllegalAccessException, InstantiationException {
		SqlSessionFactory factory = super.buildSqlSessionFactory();
		Configuration config = factory.getConfiguration();
		for (Interceptor interceptor : interceptors) {
			config.addInterceptor(interceptor);
		}
		return factory;
	}
}


下一次改进方向,现在目前来讲,只是实现了sql能分页,但缺少count的集成。mybatis中两条语句是避免不了,虽然可以用include来避免重复的where语句。


<sql id="findAllWhere">
	where mm = #{love}
</sql>

<select id="findAll" resultMap="helloResultMap">
	select * from HELLO
	<include refid="findAllWhere"/>
	order by create_time
</select>

<select id="findAllCount" resultType="long">
	select count(*) from HELLO
	<include refid="findAllWhere"/>
</select>


下次希望是,能在调用findAll的时候,自动调用findAllCount,遵守命名约定或者用注释来提示。由于我打算完全不写dao层,只写xml文件和接口,需要用到SqlSession.getMapper自动生成代理。由于期望是自动调用count方法和返回Page这样的对象。 这个可能就要看,是再代理一层,还是修改代理的实现了。入手点可以是继承Configuration,然后覆盖mapperRegistry来实现。
分享到:
评论
6 楼 icefire 2014-10-31  
yuyuanpei 写道
博主,我理解RowBounds是理想记录数从第几条到第几条的,那么在哪赋的值呢

作为参数传入,或者自己在代理生成时做处理,见http://icefire.iteye.com/blog/1036086
5 楼 yuyuanpei 2014-10-31  
博主,我理解RowBounds是理想记录数从第几条到第几条的,那么在哪赋的值呢
4 楼 springdeng 2013-03-15  
发个您写好的 Dialect 吧,好事做到底吗!
3 楼 icefire 2012-12-17  
zq0412310 写道
Dialect的实现是怎么写的啊?

参考hibernate实现
2 楼 zq0412310 2012-12-14  
Dialect的实现是怎么写的啊?
1 楼 squll369 2011-06-21  
不错,借鉴一下。

相关推荐

    06实现mybatis分页插件demo

    06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo06实现mybatis分页插件demo...

    mybatis分页插件代码

    【标题】"mybatis分页插件代码"主要涉及到MyBatis框架中的一种增强功能——分页插件的使用。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。在处理大数据量时,分页查询是必不可少的优化...

    MyBatis分页功能实现

    在实现MyBatis分页功能时,我们需要添加MyBatis、Spring以及它们的整合包(如mybatis-spring)到项目的依赖列表中。确保版本兼容,这样才能让Spring管理和初始化MyBatis的数据源、SqlSessionFactory、Mapper接口等...

    Mybatis分页拦截器

    Mybatis分页拦截器是Mybatis框架中一种实用的扩展机制,用于实现高效的数据库查询分页功能。在Mybatis中,拦截器扮演着插件的角色,可以监听并修改Mybatis执行过程中的某些行为。本话题将深入探讨Mybatis分页拦截器...

    mybatis分页完整的项目

    1. **MyBatis分页插件**:MyBatis提供了PageHelper等分页插件,可以简化分页操作,通过配置即可实现自动分页。 2. **MyBatis动态SQL**:利用MyBatis的动态SQL,可以根据条件灵活构建SQL语句,实现复杂的查询需求。 ...

    MyBatis分页插件.rar

    总的来说,MyBatis分页插件通过jsqlparser和PageHelper的结合,极大地简化了开发过程中的分页实现,提高了代码的可读性和维护性,是MyBatis开发中值得推荐的工具。在实际项目中,正确地使用这个插件可以提高开发效率...

    mybatis分页拦截器(自动封装版)剖析.pdf

    在自动封装版的MyBatis分页拦截器中,开发者通常会创建一个拦截器类,该类会拦截到执行SQL的时机,然后在SQL语句中动态添加分页相关的条件,如LIMIT和OFFSET子句,以实现数据的分页展示。 分页拦截器的核心思想是...

    mybatis分页插件的使用

    ### Mybatis分页插件详解 #### 一、概述 在使用Mybatis处理大量数据时,分页查询是一项常见的需求。传统的做法是在SQL语句中手动加入`LIMIT`和`OFFSET`来实现分页功能,这种方法不仅繁琐而且容易出错。针对这一...

    mybatis分页jar包

    自己封装的mybatis分页jar包,实现了mybatis的物理分页,目前只支持mysql和oracle两种数据库。

    MyBatis自动分页实现

    MyBatis自动分页实现 MyBatis 是一个流行的持久层框架,它提供了强大的数据访问功能。但是,在实际应用中,分页问题经常困扰开发者。要实现分页,开发者需要手动编写代码来实现分页逻辑,这不仅增加了开发难度,也...

    MyBatis分页

    《MyBatis分页详解与实战应用》 在现代Web开发中,数据的分页展示是必不可少的功能,尤其是在处理大量数据时,为了提高用户体验和优化性能,MyBatis提供了强大的分页支持。本文将深入探讨MyBatis的分页原理,并结合...

    mybatis 分页插件jar包

    总的来说,MyBatis分页插件PageHelper是Java Web开发中处理大数据量场景的有效工具,它的出现大大简化了分页实现的复杂度,提升了开发效率,并且保持了代码的整洁和可维护性。在实际项目中,合理运用PageHelper可以...

    ssm整合的jar和mybatis分页代码,mybatis生成工具类

    在本示例中,"ssm整合的jar和mybatis分页代码,mybatis生成工具类" 提供了实现SSM整合时所需的一些关键组件,包括MyBatis的分页拦截器和代码生成工具。 首先,`PaginationInterceptor.java` 文件是MyBatis的分页...

    mybatis分页插件源码

    下面我们将深入探讨MyBatis分页插件的相关知识点。 1. **MyBatis框架基础** MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果...

    MyBatis 分页

    MyBatis分页是数据库操作中的一个重要概念,它允许开发者在查询数据时限制返回结果的数量,从而提高查询效率和用户体验。在大型应用中,如果一次性加载所有数据,不仅会消耗大量内存,还可能导致用户界面反应迟钝。...

    mybatis分页(struts2+spring+mybatis)

    本项目“mybatis分页(struts2+spring+mybatis)”演示了如何在Java Web应用中实现基于MyBatis的分页功能,结合Struts2和Spring框架,提供了一个完整的解决方案。下面将详细解释这个项目涉及的知识点。 1. **...

    springboot+mybatis分页

    至此,我们就完成了SpringBoot与Mybatis的集成,并利用PageHelper实现了简单的分页查询。在实际项目中,可以根据业务需求进一步定制分页参数,如排序方式、是否包含空结果等。通过这种方式,我们可以有效地管理数据...

    mybatis 分页自己写的一个分页

    通过对`mybatis分页代码`的压缩包进行解压和研究,你可以进一步了解具体的实现细节,包括可能的错误处理、参数校验以及如何在实际项目中与Service层和Controller层进行交互。这样的分页解决方案不仅可以提高代码的可...

Global site tag (gtag.js) - Google Analytics