关于iBatis的分页性能问题,网上的讨论也很多,经过验证,我的结论是:只有在表的数据量很大,并且是从很后面的一个位置取一页数据的时候(比如从1000000条开始取100条),性能问题才比较明显。分析如下。
首先看一下iBatis的分页代码。iBatis中,具体负责执行sql的类是com.ibatis.sqlmap.engine.execution.SqlExecutor。负责分页查询的方法是executeQuery —>handleMultipleResults —> handleResults。handleResults方法的源码如下:
- 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()) {
- return;
- }
- }
- }
- // 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);
- }
- }
从代码中可以看出iBatis分页查询的逻辑是首先判断ResulteSet的类型,如果ResultSet的类型是ResultSet.TYPE_FORWARD_ONLY,则使用ResultSet对象的next()方法,一步一步地移动游标到要取的第一条记录的位置,然后再采用next()方法取出一页的数据;如果ResultSet的类型不是ResultSet.TYPE_FORWARD_ONLY,则采用ResultSet对象的absolute()方法,移动游标到要取的第一条记录的位置,然后再采用next()方法取出一页的数据。
ResultSet的类型,是在iBatis的配置文件中配置的,如:
<select id="queryAllUser" resultMap="user" resultSetType="FORWARD_ONLY">
select id,name from user_tab
</select>
其中resultSetType的可选值为FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE,如果没有配置,默认值为FORWARD_ONLY,FORWARD_ONLY类型的ResultSet 不支持absolute方法,所以是通过next方法定位的。一般情况下,我们都使用FORWARD_ONLY类型的ResultSet,SCROLL类型ResultSet的优点是可向前,向后滚动,并支持精确定位(absolute),但缺点是把结果集全部加载进缓存(如果查询是从1000000条开始取100条,会把前100万条数据也加载进缓存),容易造成内存溢出,性能也很差,除非必要,一般不使用。
可见,iBatis的分页完全依赖于JDBC ResultSet的next方法或absolute方法来实现,而Hibernate在分页查询方面,比iBatis要好很多,Hibernate可以根据不同的数据库,对sql做不同的优化加工,然后再执行优化后的sql。比如,对于Oracle数据库来说,原始sql为select * form user_tab, 从1000001条开始取100条,则hibernate加工后的sql为:
select *
from (select row_.*, rownum rownum_
from (SELECT * FROM user_tab) row_
where rownum <= 1000100)
where rownum_ > 1000000
写一个程序,对比一下两种方式下的查询效率。程序如下:
- public class Test{
- public static void main(String[] args) throws Exception {
- Class.forName("oracle.jdbc.driver.OracleDriver");
- Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:db", "db",
- "xwdb");
- long a = System.currentTimeMillis();
- testPageQuery1(conn);
- //testPageQuery2(conn);
- long b = System.currentTimeMillis();
- System.out.println(b-a);
- }
- public static void testPageQuery1(Connection conn) throws Exception{
- String sql = "SELECT * FROM user_tab ";
- Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- ResultSet rs = stmt.executeQuery(sql);
- int j=0;
- //游标移动到1000001条数据的位置
- while(rs.next() && j++<1000000){
- }
- int i=0;
- //依次取出100条数据
- while(rs.next() && i++<100){
- }
- }
- public static void testPageQuery2(Connection conn) throws Exception{
- String sql = "SELECT * FROM user_tab ";
- StringBuffer pagingSelect = new StringBuffer( sql.length()+100 );
- pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
- pagingSelect.append(sql);
- pagingSelect.append(" ) row_ where rownum <= 1000100) where rownum_ > 1000000");
- Statement stmt = conn
- .createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- ResultSet rs = stmt.executeQuery(pagingSelect.toString());
- while(rs.next()){
- }
- }
- }
testPageQuery1方法是用ibatis采用的分页方法查询,testPageQuery2是用Hibernate采用的分页方法查询,发现testPageQuery1需要执行十几秒,而testPageQuery2仅需要执行零点几秒,差异很大。而如果改成从1000条开始取100条,甚至更靠前,则2者的差别是非常小的。
综上所述,如果系统中查询的数据量很大,并且用户会选择查询非常靠后的数据,那么我们就应该替换iBatis的分页实现,如果不存在这种情况,那我们就不需要替换iBatis的分页实现,一般情况下,用户不可能去查询那么靠后的页,这也是iBatis一直不修改分页实现的原因吧。
如果我们选择替换的话,有三种办法,一种是自己写一个类,继承iBatis的SqlExecutor,然后把这个类注入到com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient中,由于SqlExecutor是ExtendedSqlMapClient的私有变量,没有public类型的set方法,所以需要采用reflect机制注入;第二种方法是在自己的工程里写一个和iBatis的SqlExecutor的包名和类名完全一样的类,web工程中,WEB-INF/classes下的java类,先于WEB-INF/lib下jar包的加载,所以就巧妙了覆盖了iBatis的SqlExecutor类;第三种办法是弃用iBatis的分页查询方法queryForList(String sql,Object obj,int maxResult,int skipResult),而用普通查询方法,queryForList(String sql,Object obj)。只不过把maxResult和skipResult都作为obj的变量传到sql里去。如下:
<select id="queryAllUser" resultMap="user">
select *
from (select row_.*, rownum rownum_
from (SELECT * FROM user_tab) row_
where rownum <= #_maxResult#)
where rownum_ > #_skipResult#
</select>
相关推荐
5. **性能优化**:在处理大数据分页时,需要关注性能问题。例如,避免使用`OFFSET`较大的分页方式,因为这会导致数据库性能下降。可以结合主键排序和范围查询优化。 6. **源码分析**:理解Ibatis的源码有助于我们更...
在描述中提到了一个博客链接,虽然具体内容没有给出,但通常博主会分享关于iBATIS分页实现的详细步骤、常见问题以及可能的解决方案。iBATIS的分页实现主要有两种方法: 1. **基于存储过程的分页**:在数据库层面...
在Ibatis和Mybatis中,分页通常通过在SQL查询语句中添加LIMIT和OFFSET子句来实现。在Mybatis中,可以通过设置Mapper XML文件中的参数,或者使用`<if>`标签来动态构建分页条件。 4. **CRUD操作**: - **Create...
### MySQL、JDBC详解及与iBatis对比 ...通过对MySQL的基本操作、JDBC的工作原理以及iBatis与JDBC的对比分析,我们可以更全面地理解数据库编程的关键概念和技术要点。希望本文能够帮助开发者们更加熟练地掌握这些技能。
- **性能优化与缓存**:了解如何利用iBATIS的缓存机制来提高应用性能,减少数据库访问次数。 - **数据访问对象(DAO)模式**:深入研究iBATIS如何支持DAO模式,简化数据访问逻辑,实现业务逻辑与数据访问的分离。 - ...
2. 分页查询:iBatis支持通过传入参数实现分页查询,提高查询效率。 3. 动态SQL:利用、、、等标签,实现条件判断和选择性执行的SQL语句。 4. 批量操作:BatchExecutor适用于批量插入、更新和删除操作,提高性能。 ...
- **日志库**:如log4j或slf4j,用于记录Ibatis的执行日志,帮助调试和性能分析。 - **其他依赖**:如ognl,用于表达式语言支持,使得在XML中可以执行更复杂的逻辑。 学习Ibatis,除了理解这些基本概念,还需要...
Ibatis允许开发者自定义插件,通过拦截器模式增强SqlSession或Executor的行为,例如日志记录、性能分析等。 这个自己编写的Ibatis框架实现,虽然可能在功能上与官方版本有所差异,但基本原理和核心思想是一致的,...
综上所述,iBatis 作为一款轻量级的 ORM 框架,在处理复杂 SQL 查询以及对性能有较高要求的应用场景中具有明显的优势。尽管它在某些高级特性和自动化程度上略逊于 Hibernate,但对于很多实际应用场景而言,iBatis 的...
**iBatis**(现为MyBatis)是一款轻量级的Java持久层框架,它解决了传统JDBC代码繁琐的问题,将SQL语句与Java代码分离,以XML或注解的方式定义在配置文件中。iBatis提供了一种映射机制,使得Java对象和数据库记录...
Ibatis插件则是为了增强Ibatis功能而设计的一系列扩展工具,它们能够帮助开发者在使用Ibatis时提高开发效率,提供诸如日志、缓存、自动填充、性能分析等功能。在本话题中,我们将主要讨论Ibatis插件的安装和使用。 ...
- **分页查询**:讲解如何实现分页查询功能。 - **多表查询**:介绍处理多表联接查询的方法。 #### 2.5 事务管理 - **事务概念**:解释事务的基本原理及其在iBATIS中的应用。 - **配置方法**:指导如何配置iBATIS以...
相比之下,iBATIS更适用于那些需要高级SQL查询技巧的应用场景,如处理复杂联表查询、分页查询等。iBATIS给予开发者对SQL语句的完全控制权,这意味着开发者可以优化查询逻辑,以适应数据库设计的特殊需求,特别是在...
源码分析部分,我们可以看到iBATIS如何解析XML配置文件,如何通过反射机制调用Java方法,以及如何将查询结果自动映射到Java对象上。这对于理解iBATIS的工作流程及其内部机制至关重要。 在"iBATIS开发指南电子书"中...
为了满足更复杂的查询需求,iBATIS提供了一系列高级查询技术,如动态SQL、分页查询等。动态SQL允许开发者根据运行时条件动态生成SQL语句,极大地提高了SQL语句的灵活性和复用性。 **2.5 事务管理** iBATIS支持多种...
例如,MySQL 和 Oracle 在语法上有差异,如分页查询、日期处理等。通过创建和注册新的方言类,可以确保 SQL 能够正确地在不同数据库上执行。 2. **数据源配置**:为了支持多种数据库,需要在 iBatis 的配置文件中...
2. **查询性能**:考察复杂查询、简单查询以及分页查询的效率。 3. **更新性能**:对比更新单个记录或多个记录的耗时。 4. **删除性能**:分析删除操作的效率。 5. **缓存效果**:如果两者都启用缓存,可以看看它们...
MyBatis 分页插件是 MyBatis 框架中的一个重要组件,它允许开发者在进行数据查询时实现高效的分页功能,而无需修改原有的 SQL 查询语句或 DAO 层代码,因此被称为“非入侵式”。这个插件的源码分析可以帮助我们深入...
7. **插件机制**:Ibatis允许开发者自定义插件,拦截SQL执行过程,实现如日志记录、性能分析等功能,扩展性极强。 8. **事务管理**:Ibatis支持手动和自动的事务管理,理解何时提交、回滚事务,以及事务的隔离级别...
本资料包中的"doc"、"rar"、"txt"文件可能是详细的教程文档、示例代码或案例分析,对于深入理解和应用Ibatis有着极大的帮助。通过阅读这些资料,你可以掌握Ibatis的各个方面,从而更好地在实际项目中运用这一强大的...