Рекурсия в Mybatis SQL, вызывающая переполнение стека при компиляции - PullRequest
0 голосов
/ 06 февраля 2019

У меня есть класс с рекурсивным отношением, как показано ниже.

   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.

Ответы [ 2 ]

0 голосов
/ 07 февраля 2019

Это не работает, потому что элемент 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>

Вам необходимо настроить проект, чтобы сделать скорость доступной.

Недостатком является то, что вы не можете связывать параметры с помощью подготовленного оператора (примечание 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;
}
0 голосов
/ 06 февраля 2019

С этой маленькой информацией я предполагаю, что это проблема с зацикливанием ссылок.Если у меня есть obj1 и в его списке есть ссылка на obj2 и наоборот, то пропущен пропущенный пробел между ними.Если это не та проблема, пожалуйста, опишите, что вы делаете со своими данными

...