Hibernate Spring数据JpaRepository saveAndFlush在@Transactional内不工作

Hibernate Spring数据JpaRepository saveAndFlush在@Transactional内不工作,hibernate,spring-data-jpa,spring-transactions,Hibernate,Spring Data Jpa,Spring Transactions,目前,我面临着SpringData/Hibernate的一个问题,我将简要介绍这个问题 我正在开发一些简单的报告功能,它将实体作为流从单个表中获取。这工作正常,但必须在运行时间较长的事务中进行,因为与数据库的连接需要保持打开状态。在这个事务中,我希望不断更新ReportEntity,它跟踪流程的状态和进度。代码基本上如下所示: @Async @Transactional(isolation = Isolation.READ_UNCOMMITTED) public void

目前,我面临着SpringData/Hibernate的一个问题,我将简要介绍这个问题

我正在开发一些简单的报告功能,它将实体作为流从单个表中获取。这工作正常,但必须在运行时间较长的事务中进行,因为与数据库的连接需要保持打开状态。在这个事务中,我希望不断更新ReportEntity,它跟踪流程的状态和进度。代码基本上如下所示:

    @Async
    @Transactional(isolation = Isolation.READ_UNCOMMITTED)
    public void process(Long reportId, SearchCriteria searchCriteriaTo) {

        ReportEntity report = reportRepository.findById(reportId).get();
        report.setStatus(ReportGenerationStatus.START);
        reportRepository.saveAndFlush(report);

        Stream<RawDataEntity> rawDataEntityStream = this.rawDataRepository
            .findAllFiltered(...);

        report.setStatus(ReportGenerationStatus.GENERATING_REPORT);
        report.setProgress(50);
        reportRepository.saveAndFlush(report);

        ...

        rawDataEntityStream.forEach(() -> DoSomething())

        ...

        report.setStatus(ReportGenerationStatus.FINISHED);
        report.setProgress(100);
    }

但“生成报告”状态不会写入数据库。只有在最终提交之后,状态才会更新为FINSIH。我试图将隔离级别设置为“READ_UNCOMMITTED”,但这也没有效果。但是,Hibernate调试日志建议触发更新:

o.h.e.i.DefaultMergeEventListener        : EntityCopyObserver strategy: disallow
o.h.e.i.AbstractFlushingEventListener    : Processing flush-time cascades
o.h.e.i.AbstractFlushingEventListener    : Dirty checking collections
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
o.hibernate.internal.util.EntityPrinter  : Listing entities:
o.hibernate.internal.util.EntityPrinter  : com.capgemini.cusmesonlinereporting.reportmanagement.dataaccess.api.ReportEntity{downloadLink=null, externalUserId=MMUSTER, modificationCounter=0, creationTimestamp=2020-03-26T07:21:03.421700Z, name=2018-1-1_2020-3-23, progress=0, id=1000019, key=ecb0955f-b838-4f30-ac1f-dc94763ac810, status=GENERATING_REPORT}
org.hibernate.SQL                        : update Report set modificationCounter=?, creationTimestamp=?, downloadLink=?, externalUserId=?, key=?, name=?, progress=?, status=? where id=? and modificationCounter=?
Hibernate: update Report set modificationCounter=?, creationTimestamp=?, downloadLink=?, externalUserId=?, key=?, name=?, progress=?, status=? where id=? and modificationCounter=?
我可以继续使用“REQUIRE_NEW”事务在嵌套事务中对ReportEntity进行所有更新,但我不想因为长时间运行的事务可能会并行执行多次而陷入潜在的死锁。 因此,我的问题是:

  • 为什么saveAndFlush在这种情况下不起作用
  • 是否有不涉及创建嵌套事务的变通方法

  • 你的理解是错误的。Flush将向DB发送挂起的更改,但仅在当前事务中发送。它不会提交更改。在数据库中保存内容的唯一方法是使用事务,因此您需要更多事务或需要更好的处理方法。好的,但我认为“Isolation.READ_UNCOMMITTED”应该正好解决这个问题。Spring文档中写道:“这个级别允许一个事务更改的行在提交该行中的任何更改之前被另一个事务读取。”同样,您的理解是错误的。在提交事务之前,不会保留任何内容。即使是隔离级别,如果出现问题,更改也将回滚(其他人将读取脏数据,也称为dirte read!)。因此,您唯一的解决方案是增加事务。此外,您的处理似乎有缺陷,因为您正在一次读取内存中的所有内容,您有一个用于批处理的大型事务(不要或分块处理和刷新/清除)。这样做会大大提高你的表现。特别是在使用和ORM时。本质上,我正在尝试使用JPA存储库来流式传输数据,以避免将所有数据都存储在内存中,方法如下所述。目前我正在评估这是否适合我。提交回事务:我知道,在提交之前,没有任何东西是分散的,但是如何从其他事务中进行脏读呢?两个事务是否都需要对同一实体对象的引用?因为允许其他事务读取未提交的数据。假设您将bankaccount的余额更改为200(但尚未提交),其他一些事务会读取该值,然后再增加100。然而与此同时,你把余额减少到了100。因此,他阅读了肮脏的数据。同样,流式传输也不会有多大帮助,这里的主要问题是保存所有更改的大型事务。每次执行选择或操作时,都会执行脏检查,这将随着每个已更改的条目显著增加。这就是为什么你应该定期冲洗和清理。
    o.h.e.i.DefaultMergeEventListener        : EntityCopyObserver strategy: disallow
    o.h.e.i.AbstractFlushingEventListener    : Processing flush-time cascades
    o.h.e.i.AbstractFlushingEventListener    : Dirty checking collections
    o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 insertions, 1 updates, 0 deletions to 1 objects
    o.h.e.i.AbstractFlushingEventListener    : Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
    o.hibernate.internal.util.EntityPrinter  : Listing entities:
    o.hibernate.internal.util.EntityPrinter  : com.capgemini.cusmesonlinereporting.reportmanagement.dataaccess.api.ReportEntity{downloadLink=null, externalUserId=MMUSTER, modificationCounter=0, creationTimestamp=2020-03-26T07:21:03.421700Z, name=2018-1-1_2020-3-23, progress=0, id=1000019, key=ecb0955f-b838-4f30-ac1f-dc94763ac810, status=GENERATING_REPORT}
    org.hibernate.SQL                        : update Report set modificationCounter=?, creationTimestamp=?, downloadLink=?, externalUserId=?, key=?, name=?, progress=?, status=? where id=? and modificationCounter=?
    Hibernate: update Report set modificationCounter=?, creationTimestamp=?, downloadLink=?, externalUserId=?, key=?, name=?, progress=?, status=? where id=? and modificationCounter=?