Mybatis SQL 中的递归导致编译时堆栈溢出

Recursion in Mybatis SQL causing stack overflow while compile

我有一个 class 具有如下递归关系。

   Class A{
      String id;
      String op;
      String val;
      List<A> aList;
   }

我正在使用 MySQL MyBatis 生成如下查询。


 <sql id="testRecursion">
        <foreach collection="A.aList" item="aList" open="(" separator=" UNION " close=")">
            <if test="aList.op == null">
                (
                SELECT sum(val) as val FROM
                FROM test_data
                WHERE id = #{aList.id} 
                )
            </if>

            <if test="aList.op== 'AND'">
                SELECT max(val) as val FROM
                <include refid="testRecursion"/>
            </if>

            <if test="aList.op== 'OR'">
                SELECT min(val) as val FROM
                <include refid="testRecursion"/>
            </if>
       </foreach>
  </sql>

在编译时出现如下错误,堆栈溢出异常。

at org.apache.ibatis.builder.xml.XMLIncludeTransformer.applyIncludes(XMLIncludeTransformer.java:74) ~[mybatis-3.4.5.jar:3.4.5]
at org.apache.ibatis.builder.xml.XMLIncludeTransformer.applyIncludes(XMLIncludeTransformer.java:62) ~[mybatis-3.4.5.jar:3.4.5]

谁能帮我看看,如何在mybatis中实现递归。

根据这些信息,我认为这是循环引用的问题。如果我有 obj1,并且在他的列表中引用了 obj2,反之亦然,那么 foreaching 会在两者之间反弹。 如果不是那个问题,请详细说明您对数据做了什么

这是行不通的,因为 include 元素不支持递归,也就是说,您不能从自身包含 sql 片段。

如果您切换到速度脚本引擎,您可以使用允许引用自身的宏来实现您需要的功能。

不幸的是,您没有为您的代码段提供调用代码,因此我的示例可能会有所不同,您需要对其进行调整,但它应该能让您明白。

给定 A 的定义:

public class A {
  Integer id;
  String op;
  Integer val;
  List<A> nodes;
  // setters/getters omitted for brevity

您可以这样定义映射器:

class MyMapper {
   Integer getRecursive(@Param("A") A a);
}

并在xml中查询:

<select id="getRecursive" lang="velocity" resultType="int">
    #macro(node $a)
        #if( ! $a.op )
            SELECT sum(val) as val FROM A
            WHERE id = ${a.id} 
        #else
            SELECT
            #if( $a.op == "AND" )
                max(val) as val
            #else
                min(val) as val
            #end
            FROM (
                #repeat( $a.nodes $aNode "UNION ALL" )
                   #node($aNode)
                #end
            )
        #end
    #end

    select val FROM (
        #node($_parameter.A)
    )
</select>

您需要 configure 项目才能使速度可用。

缺点是您不能通过准备好的语句绑定参数(注意 id = ${a.id} 而不是 id = @{a.id})。

下面是我调用映射器方法的方式:

@Test
public void testRecursive() {
    A a = or(leaf(1), and(leaf(2), leaf(3)));
    assertThat(sut.getRecursive(a), equalTo(1));
}

private A leaf(int id) {
    A a = new A();
    a.setId(id);
    return a;
}

private A or(A ... ops) {
    return operation("OR", ops);
}

private A and(A ... ops) {
    return operation("AND", ops);
}

private A operation(String operation, A ... ops) {
    A a = new A();
    a.setOp(operation);
    a.setNodes(Arrays.asList(ops));
    return a;
}