Currently I am facing a problem with Spring-Data/Hibernate that I will introduce briefly.
I am working on some simple Reporting functionality, that fetches Entities from a single table as Stream. This is working fine, but has to happen within a longer running Transaction, because the Connection to the Database needs to stay open. Within this transaction I want to constantly update a ReportEntity, which tracks the status and the progress of the process. Code looks basically like this:
@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);
}
My problem is, that from my understanding saveAndFlush should write the intermediate steps of The ReportEntity into the database, but it does not. I am using a Posgres DB and verified this with PGAdmin and also I wrote a polling client that constantly fetches the ReportEntity.
@Transactional(readOnly = true)
public ReportEto getReportByKey(UUID key) {
// entityManager.clear();
ReportEntity reportEntity = this.reportRepository.findByKey(key);
return getBeanMapper().map(reportEntity, ReportEto.class);
}
But the status "GENERATING_REPORT" does not get written to the DB. Only after the final commit the status is updated to FINSIH. I tried to set the isolation level to "READ_UNCOMMITTED", but this also does not have an effect. The Hibernate Debug Logs however suggest, that a Update is triggered:
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=?
I could go ahead and make all the updates to the ReportEntity in a nested Transaction with a "REQUIRE_NEW" Transaction, but I dont want to run into potential DeadLocks because of the long running Transaction that might get executed multiple times in parallel. So my questions are:
- Why does saveAndFlush not work in this case ?
- are there any workarounds that do not involve creating nested transactions?