В настоящее время я сталкиваюсь с проблемой Spring-Data / 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);
}
Моя проблема в том, что, насколько я понимаю, saveAndFlu sh должно записать промежуточные шаги объекта ReportEntity в базу данных, но это не так. Я использую базу данных Posgres и проверил это с помощью PGAdmin, а также написал клиент опроса, который постоянно извлекает ReportEntity.
@Transactional(readOnly = true)
public ReportEto getReportByKey(UUID key) {
// entityManager.clear();
ReportEntity reportEntity = this.reportRepository.findByKey(key);
return getBeanMapper().map(reportEntity, ReportEto.class);
}
Но статус «GENERATING_REPORT» не записывается в БД. Только после окончательной фиксации статус обновляется до 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=?
Я мог бы go вперед и сделать все обновления для ReportEntity во вложенной транзакции с транзакцией REQUIRE_NEW, но я не Я хочу столкнуться с потенциальными DeadLocks из-за длительной транзакции, которая может выполняться несколько раз параллельно. Итак, мои вопросы:
- Почему saveAndFlu sh не работает в этом случае?
- Существуют ли обходные пути, не связанные с созданием вложенных транзакций?