首页 > 解决方案 > 为什么保存方法需要使用已经存在的EntityManager?

问题描述

最近我遇到了一个关于从方法更新实体的问题,即使从方法调用时它可以无缝工作@Scheduled,它也会失败并出现异常。这是相关的例子:org.hibernate.TransientPropertyValueException: object references an unsaved transient instance@RestController

有问题的方法(为简洁起见,省略了类的其他部分):

@Service
public class AnonymizationService
{
    private final ItemRepository itemRepository;

    public Result anonymizeItemsOlderThan(int days) {
        List<Item> data = itemRepository.findAllByCreatedDateBeforeAndAnonymizationDateIsNull(Instant.now().minus(days, ChronoUnit.DAYS));

        List<String> itemsAnonymized = new ArrayList<>(data.size());

        data.forEach(item -> itemsAnonymized.add(itemRepository.save(item.anonymize()).getRequestId()));

        return Result.builder().anonymizedItems(itemsAnonymized).build();
    }
}

@RestController调用者(同样省略了大部分内容):

@RestController
public class DataAnonymizationAPI
{
  private final AnonymizationService anonymizationService;

  @PutMapping(path = "${datadeletion.path:/anonymize}", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<Result> anonymizeAll(@Valid DataDeletionRules dataDeletionRules) {
    return ResponseEntity.ok(anonymizationService.anonymizeItemsOlderThan(dataDeletionRules.getMinimunAge()));
  }
}

同样,当像上面那样使用时,它工作得很好。当AnonymizationService#anonymizeItemsOlderThan()从以下@Scheduled方法调用时会出现问题:

@Component
public class DataDeletionTasks
{

  private final AnonymizationService anonymizationService;
  private final DataAnonymizationProperties properties;

  @Scheduled(cron = "${datadeletion.anonymization.schedule}")
  public void anonymizeItemsPeriodically() {
    anonymizationService.anonymizeItemsOlderThan(properties.getAnonymization().getMinAge());
  }
}

在这种情况下,它会失败,但上面提到的异常 ( org.hibernate.TransientPropertyValueException)。

将日志级别更改为 DEBUG 并仔细分析后,没有任何意外发生:

o.s.orm.jpa.JpaTransactionManager        : Found thread-bound EntityManager [SessionImpl(1702787226<open>)] for JPA transaction
o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager        : Creating new transaction with name [org.springframework.data.jpa.repository.support.SimpleJpaRepository.saveAndFlush]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
o.s.orm.jpa.JpaTransactionManager        : Opened new EntityManager [SessionImpl(644498403<open>)] for JPA transaction

当然,我的直觉是添加立即解决它@TransactionalAnonymization#anonymizeItemsOlderThan()方法,但为什么呢?

为什么它在一种情况下有效,而在另一种情况下无效?为什么saveAndFlush()必须首先使用EntityManager用于检索实体的相同来执行?

这种情况让我觉得我的知识在一个非常基础的层面上是有缺陷的,但不知怎的却找不到一个明确的解释。无论如何,请随时向我指出可能对我有帮助的相关文献。

标签: javaspringhibernatejpaspring-scheduled

解决方案


推荐阅读