3

简要问题描述

遵循多个结果集的指南并在此答案的帮助下,我现在能够提取 2 个不同的记录集,但它们只是列表,并没有映射到结果对象上。

详细介绍

我有课程(简化):

public class SupplyChain{
    private String id;

    private List<SupplyChainNode> nodes;
    private List<SupplyChainEdge> edges;
}

public class SupplyChainNode {
    private String id;
    private String label;
}

public class SupplyChainEdge {
    private String id;
    private String label;
}

MyBatis接口声明如下:

public interface SupplyChainMBDao {
    List<SupplyChain> getByPartyId(@Param("partyId") String partyId);
}

MyBatis映射:

<mapper namespace="ru.rlh.egais.portal.backend.dao.mybatis.SupplyChainMBDao">
    <select id="getByPartyId" resultSets="edges,nodes" resultMap="supplyChainMapEdge, supplyChainMapNode"><![CDATA[
    -- There big query returns 2 recordset - first for Edges and second for Nodes. They have different amount of rows and logic of obtain, but share initial computation and it is desire to return them atomic.
    -- Let it be for simplicity:
    SELECT * FROM (VALUES(1, 2)) edges(id, label);

    SELECT * FROM (VALUES(2, 3), (4, 5)) nodes(id, label)
    ]]></select>

    <resultMap id="supplyChainMapEdge" type="ru.rlh.egais.portal.api.dto.bo.supplychain.SupplyChainEdge" >
        <result property="label" column="label"/>
    </resultMap>

    <resultMap id="supplyChainMapNode" type="ru.rlh.egais.portal.api.dto.bo.supplychain.SupplyChainNode" >
        <result property="label" column="label"/>
    </resultMap>
</mapper>

所以,基本上它可以工作,我得到了 2 个集合。List<SupplyChain>但是,我真正得到了List<List>内部列表在运行时包含 2 个元素的位置,而不是声明的返回值:

  • 0 元素是List<SupplyChainEdge>
  • 第一个:List<SupplyChainNode>

如何将这些原始集合包装到对象中SupplyChain

4

2 回答 2

2

另一种方法:使用自定义 ResultHandler。我一直调试到 DefaultResultSetHandler.handleResultSets 并发现提供的自定义结果处理程序用于每个“子”结果集而不是全局查询。然后结果列表必须直接构建在预期的位置:在 supplyChain 对象中。

/* the Custom Result Handler  */
public class CustomResultHandler implements ResultHandler {
    private SupplyChain supplyChain;

    public CustomResultHandler(SupplyChain supplyChain) {
        this.supplyChain = supplyChain;
    }
    @Override
    public void handleResult(ResultContext ctx) {
        Object o = ctx.getResultObject();
        /* access to SupplyChain members is simplified here */
        if (o instanceof SupplyChainEdge) {
            SupplyChainEdge sc = (SupplyChainEdge) o;   
            if (ctx.getResultCount() == 1) { /* lazy list instantiation */
                this.supplyChain.supplyChainEdge = new ArrayList<SupplyChainEdge>();
            }
            this.supplyChain.supplyChainEdge.add(sc);
        } else if (o instanceof SupplyChainNode) {
            SupplyChainNode sc = (SupplyChainNode) o;
            if (ctx.getResultCount() == 1) { /* lazy list instantiation */
                this.supplyChain.supplyChainNode = new ArrayList<SupplyChainNode>();
            }
            this.supplyChain.supplyChainNode.add(sc);
        }   
    }
}

/* in mapper interface  */
void getByPartyId(@Param("partyId") String partyId, ResultHandler handler);

/* how to call it */
SupplyChain supplyChain = new SupplyChain();
ResultHandler handler = new CustomResultHandler();
mapper.getByPartyId(id, handler);

我希望这符合您的期望。无论如何,我认为这是对这个问题的回答:将集合包装到对象 SupplyChain 中。

干杯

于 2016-10-27T15:28:26.970 回答
2

我不得不猜测“大查询返回 2 个记录集 [...]”实际上是一个存储过程,其主体包含 2 个 SELECT 语句(正如Mybatis doc的Multiple ResultSets for Association章节中所建议的那样),这就是你获得 2结果集。

您可能确实尝试构建单个 SELECT 并使用集合/关联映射列。

或者您可以将您的目标集合绑定到存储过程的 OUT 参数(这里适用于 Oracle):

CREATE PROCEDURE p(partyId IN VARCHAR2, rc1 OUT SYS_REFCURSOR, rc2 OUT SYS_REFCURSOR) AS
BEGIN
OPEN rc1 FOR SELECT [...];
OPEN rc2 FOR SELECT [...];
END;

这是 Mapper 接口(带有注释,您可以将其全部转置为 XML):

@Select("{ CALL p(#{partyId}),
#{supplyChain.nodes, mode=OUT, jdbcType=CURSOR, javaType=java.sql.ResultSet, resultMap=supplyChainMapNode},
#{supplyChain.edges, mode=OUT, jdbcType=CURSOR, javaType=java.sql.ResultSet, resultMap=supplyChainMapEdge} 
}")
@Options(statementType = StatementType.CALLABLE)
void getByPartyId(@Param("partyId") String partyId, @Param("supplyChain") SupplyChain supplyChain);

resultMaps 是您在 XML 中定义的那些。

所以调用映射器,当过程响应时,SupplyChain bean 将被填充。

无论如何,我想知道使用 SELECT 或 CURSOR 之间的行为区别是什么,尤其是在资源管理/性能方面,考虑到 Java 最终可以使用 resultSet。例如,我想为 fetchSize 选项设置一个自定义值(Oracle 默认值为 10,这对于大型结果集来说非常慢(太多的 java<-->DB 往返))。但到目前为止,我无法弄清楚这个语句选项是否用于绑定输出参数。

干杯。

于 2016-10-27T09:41:58.123 回答