2

我有需要使用 MyBatis 调用的存储过程。无论如何,我设法调用了这个存储过程。过程有多个输出参数。out 参数之一是 oracle cursor。我需要遍历 Oracle Cursor,但是当我在没有使用 fetchSize 属性对 jdbc 驱动程序进行任何微调的情况下执行此操作时,它会逐行进行,并且此解决方案非常慢。我可以设置过程调用 fethcSize 属性:

<select id="getEvents" statementType="CALLABLE" parameterMap="eventInputMap" fetchSize="1000">
    {call myProc(?, ?, ?, ?, ?)}
</select>

但这根本没有帮助。我认为这不起作用,因为有多个输出参数 - 所以程序不知道应该在哪里应用这个提取大小 - 哪个输出参数。有没有办法在 ResultSet(Oracle 游标)上设置获取大小?就像我使用 java.sql 包中的 CallableStatemen 一样,我可以设置 ResultSet 获取大小。

以下是程序中的映射文件和过程调用:

  <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//ibatis.apache.org//DTD Mapper 3.0//EN"
          "http://ibatis.apache.org/dtd/ibatis-3-mapper.dtd">

<mapper namespace="mypackage.EventDao">

    <resultMap id="eventResult" type="Event">
        <result property="id" column="event_id" />
        <result property="name" column="event_name" />
    </resultMap>

    <parameterMap id="eventInputMap" type="map" >
        <parameter property="pnNetworkId" jdbcType="NUMERIC" javaType="java.lang.Integer" mode="IN"/>
        <parameter property="pvUserIdentityId" jdbcType="VARCHAR" javaType="java.lang.String" mode="IN"/>
        <parameter property="result" resultMap="eventResult" jdbcType="CURSOR" javaType="java.sql.ResultSet" mode="OUT" />
        <parameter property="success" jdbcType="INTEGER" javaType="java.lang.Integer" mode="OUT"/>
        <parameter property="message" jdbcType="VARCHAR" javaType="java.lang.String" mode="OUT"/>
    </parameterMap>



    <select id="getEvents" statementType="CALLABLE" parameterMap="eventInputMap" fetchSize="1000">
        {call myProc(?, ?, ?, ?, ?)}
    </select>
   </mapper>    

并从程序调用:

SqlSession session = sqlSessionFactory.openSession();
    Map<String, Object> eventInputMap = new HashMap<String, Object>();
        try {       
            EventDao ed = session.getMapper(EventDao.class);            
            eventInputMap.put("pnNetworkId", networkId);
            eventInputMap.put("pvUserIdentityId", identityId);          
            eventInputMap.put("success", 0);
            eventInputMap.put("message", null);         
            ed.getEvents(eventInputMap);
            session.selectList("EventDao.getEvents", eventInputMap);    
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            session.close();
        }

提前致谢!

4

1 回答 1

1

提供的代码有效。我什至检查了 3 种编写它的方法:就像这里使用parameterMap,没有parameterMap(直接在语句中映射),以及通过注释,一切正常。

我曾经认为fetchSize设置不会从主语句传播到 OUT 参数结果集,直到最近我真正测试了它。要了解是否使用了 fetch size 以及它有多大的影响,结果必须包含足够多的行数。当然,最差的是从应用程序到数据库的延迟,效果更明显。对于我的测试,过程使用的游标返回了 120 列的 5400 行(但最重要的是行数)。为了给出一个数量级,我测量了获取时间,即从存储过程返回到语句返回,结果列表中填充了从游标中获取的数据。然后我记录映射的第一个对象的实例化,这发生在全局获取开始附近,可能在第一次获取之后:

public static boolean firstInstance = true;

public Item() {
    if (firstInstance) {
        LOGGER.debug("Item first instance");
        firstInstance=false;
    }
}

在session.selectList返回后,我在最后再次登录。

这仅用于测试目的。不要让它成为你的代码。找到一个干净的方法来做到这一点。

以下是一些时间,具体取决于配置的提取大小:

- fetchSize=1    => 13000 ms
- fetchSize=10   =>  5300 ms
- fetchSize=100  =>  3800 ms
- fetchSize=300  =>  3700 ms
- fetchSize=500  =>  3650 ms
- fetchSize=1000 =>  3600 ms

Oracle JDBC 驱动程序默认 fetchSize 为 10。

使用fetchSize=1进行测试可以证明使用了提供的设置。

100,在这里,30% 被保存。除此之外,收益可以忽略不计(使用此用例和环境)

无论如何,能够知道过程执行何时完成以及结果获取何时开始会很有趣。不幸的是,Mybatis 的日志很少。我认为自定义结果处理程序可能会有所帮助,但是查看类org.apache.ibatis.executor.resultset.DefaultResultSetHandler的源代码,我注意到与允许使用自定义结果处理程序的方法handleResultSet(用于简单的选择语句)不同,方法handleRefCursorOutputParameter(此处用于过程 OUT 游标)没有。然后无需尝试传递自定义结果处理程序:它将被忽略。如果有人有解决方案,我对解决方案感兴趣。但似乎需要一个进化请求。

于 2016-11-18T13:55:54.050 回答