【问题标题】:Spring JPA deleteInBatch causes StackOverflowSpring JPA deleteInBatch 导致 StackOverflow
【发布时间】:2015-06-21 02:15:59
【问题描述】:

我有一个使用 deleteInBatch 从数据库中删除项目的问题。 我有一个对象 A 有一个对象 B 的列表,例如:

class A {
private List <B>;
}

列表包含超过 7k 个元素。 所以现在我必须删除 A 及其所有元素。我尝试通过 deleteInBatch 但我得到了

org.springframework.web.util.NestedServletException: Handler processing failed;
nested exception is java.lang.StackOverflowError

使用 sipmle delete 方法删除项目有效,但需要 5 分钟以上。 我的删除代码是:

public void delete(Long id) {
A a = repository.findOne(id);
deleteElements(a);
repository.delete(a);
}

private void deleteElements(A a) {
repository.deleteInBatch(a.getListOfB);
}

是否有一个很好的解决方案来加快删除过程或如何更改以使 deleteinbatch 不占用所有休眠堆栈 - 而不增加它?

完整的 Stacktrace:

org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.StackOverflowError
org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1259)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:827)
javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:163)
net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:206)
net.bull.javamelody.MonitoringFilter.doFilter(MonitoringFilter.java:179)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)

等等……

root cause

java.lang.StackOverflowError
org.hibernate.hql.internal.ast.tree.SqlNode.<init>(SqlNode.java:34)
sun.reflect.GeneratedConstructorAccessor36.newInstance(Unknown Source)
sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
java.lang.reflect.Constructor.newInstance(Constructor.java:526)
java.lang.Class.newInstance(Class.java:379)
org.hibernate.hql.internal.ast.SqlASTFactory.create(SqlASTFactory.java:256)
antlr.ASTFactory.create(ASTFactory.java:153)
antlr.ASTFactory.create(ASTFactory.java:186)
org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2018)
org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2026)
org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.logicalExpr(HqlSqlBaseWalker.java:2026)

等等……

【问题讨论】:

  • 你能发布完整的堆栈跟踪吗?

标签: java spring hibernate spring-data-jpa querydsl


【解决方案1】:

在您的情况下,删除查询将由JpaRepository 翻译成如下所示。

delete from [table_name] where [criteria] = id or [criteria] = id (and so on...)

jvm 抛出堆栈溢出错误,因为 HqlSqlBaseWalker 正在尝试搜索所有 or(或基本上是 where 条件)语句

我想,在您的情况下,您可以尝试生成自己的删除查询然后执行它,或者您可以尝试将数据拆分为几个列表。

【讨论】:

  • 那么,你的意思是“批处理”并没有真正在内部创建 JDBC 批处理语句?这对于任何正在调整游标缓存的 DBA 来说都是个坏消息……
  • @LukasEder 好吧,我认为问题在于这里的“批处理”术语本身。我的意思是,在这种情况下,我们通常期望的是休眠将拆分数据并尝试插入这些数据,例如每 100 个数据查询一次。但是,如果您阅读 api 文档,它确实提到该函数确实会创建一个 query。所以,我认为这里的教训是我们应该三思而后行来命名我们的公共 api ;)
  • 是的,我创建了自己的删除查询。这个过程花了 3 分钟,但我在键上创建了一个索引,效果很好。谢谢 :)
【解决方案2】:

我遇到了同样的问题 (java.lang.StackOverflowError),并使用我自己的删除查询创建了一个方法,例如deleteByIdAndYear(...),避免它。

【讨论】:

    【解决方案3】:

    我最终将deleteInBatchan answer to another question 结合起来:

    import com.google.common.collect.Lists;
    ...
    Lists.partition(messages, 500)
            .forEach(fragmentMessageDao::deleteInBatch);
    

    对我来说,这提供了一个适当而优雅的解决方案,因为我已经需要数据进行另一次计算

    【讨论】:

      【解决方案4】:

      在我的情况下,删除更小块的实体会有所帮助。 100 个项目的块工作得很好。

      【讨论】:

        猜你喜欢
        • 2020-02-04
        • 1970-01-01
        • 2014-11-26
        • 1970-01-01
        • 2011-07-20
        • 1970-01-01
        • 1970-01-01
        • 2018-05-29
        • 1970-01-01
        相关资源
        最近更新 更多