【问题标题】:javax.persistence.TransactionRequiredException while performing inserts in a loop在循环中执行插入时出现 javax.persistence.TransactionRequiredException
【发布时间】:2016-12-13 22:51:06
【问题描述】:

我有一个使用 Spring、Hibernate 和 JTA 的应用程序。

我们收到的请求包含超出预期的数据(10000 个元素),一般请求为 10-100 个元素。

在处理这个请求时,我们尝试在 DB 中为每个元素插入一条记录,这是在 for 循环中完成的

伪代码:

processRecords( list){

for (Element element: list){

dao.findBy -- This takes around 100 ms

-- some other checks and logic

dao.add(element); -- This takes around 150 ms
dao.flush();
 }

}

这个块需要很多时间来处理记录,然后我得到 *

"javax.persistence.TransactionRequiredException: 没有事务在 进步”

*

我尝试将刷新移出 for 循环,但没有帮助,我尝试研究 hibernate 的批量插入,但这是一个具有大量自定义的大型应用程序,我不认为这是一个选项,因为它会影响整个应用程序,我还尝试查找配置的事务持续时间在哪里,我能找到的唯一地方是 weblogic 上的 JTA,它设置为 480 秒。

任何关于如何解决这种情况的指标将不胜感激。

编辑: 增加 weblogic 中的 JTA Timeout 暂时解决了这个问题,但是我将它设置为一个非常大的值 5000 秒,有没有提高性能,因为我只是插入 8K 记录(我知道批处理是一种选择,但是有是自定义“框架”中的一些限制)

【问题讨论】:

  • 你使用 JPA 还是原生 hibernate。请提供持久化数据的代码 sn-p。为了持久化数据,您需要在 JDBC/DB 级别进行事务处理,如果您使用 spring 管理的 hibernate/JPA,您通常通过将 @Transactional 添加到您的 dao 方法来做到这一点。
  • 我们在 jms 队列中收到请求,并在从队列中读取消息(入站 jms)时添加了@Transactional,整个事务从那里开始
  • 你的数据库是什么?似乎数据更改量对于您的提供商来说太多了,就像它无法将其之前的图像数据保存在它拥有的任何临时存储中,然后事务会自动回滚。打开 Hibernate 调试并查看那里是否发生任何事情也可能会有所帮助。
  • 您在配置事务管理器时是否设置了超时属性?
  • 根据具体情况,有许多可能的解决方案。批量获取数据应该已经对您的情况有所帮助。在每个元素之后不冲洗也可能有所帮助。也许您可以使用多线程解决方案?

标签: java spring hibernate jta


【解决方案1】:

使用@Transactional 来控制事务。

  1. 确保不使用 PROPAGATION_NEVER 自动启动事务
  2. 通过调用带有 REQUIRES_NEW 的服务方法,为列表中的每个项目(或列表中的项目子集)启动新事务。

在第二步中,您可能希望调用另一个 bean 以便实际提取事务注释,不记得这是否适用于同一对象上的方法。

【讨论】:

  • "为列表中的每个项目启动新事务" 这可能需要一段时间...
  • 是的,如有必要,列表中的多个项目应组合在一起以提高性能。
  • 既然你的情况很直接,让Spring来处理事务。使用@Transactional 应该会有所帮助
【解决方案2】:

根据您的内联评论,您的事务持续时间为 2,500 秒,其中 1,000 秒正在读取数据库,1,500 秒正在添加到数据库中。

您可以通过稍微重构您的逻辑来减少数据库事务:在大多数数据库驱动程序中,数据库在第一次“写入”数据库之前实际上不会启动事务。您可以通过重组逻辑来利用这一点,先进行所有读取,然后再进行所有写入。我已经通过非 JPA 数据访问的这种方法取得了成功,但还没有在 JPA 上尝试过。重组后的伪代码是这样的:

processRecords( list){

// do all the reads
List adds=new ...
for (Element element: list){
  dao.findBy -- This takes around 100 ms
  -- some other checks and logic
  adds.put(element);
}

// do all the writes
for (Element element: adds){
  dao.add(element); -- This takes around 150 ms
  dao.flush();
 }

}

使用数据库跟踪来验证它是否按预期工作。除了减少事务持续时间之外,这种方法还降低了由于在另一个线程上读取而导致的阻塞风险。

【讨论】:

    猜你喜欢
    • 2023-03-07
    • 2013-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    相关资源
    最近更新 更多