java - Mybatis SQL中的递归导致编译时堆栈溢出
问题描述
我有一个具有递归关系的类,如下所示。
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 中实现递归。
解决方案
这不起作用,因为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;
}
推荐阅读
- java - 使用 Cassandra 的 Java 驱动程序的多键空间
- ajax - 如何使用单选按钮单击 MVC 触发 Ajax 功能
- vba - VBA - 保存宏命名工作簿
- android - Android Vuforia Native SDK 摄像头方向 Front
- rest - 使用 SharePoint REST 讨论“LastReplyBy”
- ios - 调节 UITableView 滚动的最大速度?
- angular - 在 ng-Template 中渲染动态 HTML
- c# - C# WPF 组合框 TextElement.Foreground 绑定
- vba - 如何在不使用特定单元格的情况下记录宏范围?
- c++ - Mac OS 上的 Pyfasttest 安装失败:致命错误:找不到“随机”文件