Mybatis 是一款优秀的 ORM 框架,它能够帮助程序员快速、简单地完成 Java 对象与关系数据库的互相映射。它提供了各种查询方式,其中一种比较有特色的查询方式就是使用 ResultHandler 进行查询,实现结果流式输出。

  有一种场景,查询结果有上百万条数据,如果在查询时把整个结果集一次性查询得到,存储于 List 返回的话,很可能会因为结果集太大,导致内存溢出(OOM)。此时我们可以采用分页查询或者可以利用 Mybatis 中的 ResultHandler 来实现流式输出。

  ResultHandler 接口只有一个方法 handleResult,我们可以实现这个方法去处理每一条 SQL 查询返回的数据。下面将详细介绍 Mybatis 中 ResultHandler 的用法。

一、ResultHandler 简介

  Mybatis 中的 ResultHandler 相当于数据结果集的处理器,它是一个回调函数(Callback),用来处理每一行数据的结果,这个回调函数可以在查询结果处理到一定量时触发,对结果集数据进行定制化处理。

  ResultHandler 的使用可以大幅提升数据处理的效率,当我们需要处理大量的数据时,一般会使用 ResultHandler 来进行结果的处理,避免一次查询就全部返回结果,浪费内存资源或造成 OOM。

二、ResultHandler 实现结果流式输出(两种写法)
2.1 创建 TestResultHandler 类,实现 ResultHandler 接口

  创建 TestResultHandler 类,实现 ResultHandler 接口,重写 handleResult 方法,实现具体的业务逻辑,例如简单的打印或输出到文件等。其中,TblTestUser 是映射数据库表格的实体类。
  或者不创建该类,在查询时直接创建 ResultHandler 类,具体见 2.4 小节中的两种服务层写法。

package com.test.handler;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import com.test.model.TblTestUser;

@Slf4j
public class TestResultHandler implements ResultHandler<TblTestUser> {

    @Override
    public void handleResult(ResultContext<? extends TblTestUser> resultContext) {
        TblTestUser tblTestUser = resultContext.getResultObject();

        // 处理数据的逻辑
        try {
            // 打印每行日志
            log.info("userNo : [{}]", tblTestUser.getUserNo());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

2.2 TblTestUserMapper 接口,定义查询方法
package com.test.dao;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.session.ResultHandler;

public interface TblTestUserMapper {

	// SQL 返回值为 void,所以我们并没有接收这个返回值。所以不会产生大对象
	// 方法中的第一个参数是查询时的限制条件,第二个是结果处理器
    void selectByResultHandler(@Param(value = "actNo") String actNo,
                               ResultHandler resultHandler);
}

2.3 XML 语法

  xml 查询方法和常规查询方法写法一致。

  <select id="selectByResultHandler" resultMap="BaseResultMap">
    SELECT
    <include refid="Base_Column_List" />
    FROM tbl_test_user
  </select>
2.4 服务层写法
package com.test.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import com.test.dao.TblTestUserMapper;
import com.test.model.TblTestUser;

import javax.annotation.Resource;

@Service
@Slf4j
public class TestService {

    @Resource
    private TblTestUserMapper tblTestUserMapper;

    /**
     * 写法 1
     *
     * @param actNo 筛选条件:活动号
     */
    @Async
    public void test1(String actNo) {
        log.info("ResultHandler1 处理查询结果");
        TestResultHandler testResultHandler = new TestResultHandler();
        tblTestUserMapper.selectByResultHandler(actNo, testResultHandler);
    }

    /**
     * 写法 2
     *
     * @param actNo 筛选条件:活动号
     */
    @Async
    public void test2(String actNo) {
        log.info("ResultHandler2 处理查询结果");
        tblTestUserMapper.selectByResultHandler(actNo, new ResultHandler<TblTestUser>() {
            @Override
            public void handleResult(ResultContext<? extends TblTestUser> resultContext) {
                TblTestUser tblTestUser = resultContext.getResultObject();

                // 处理数据的逻辑
                try {
                    // 打印每行日志
                    log.info("userNo : [{}]", tblTestUser.getUserNo());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐