Используя H2 и JPA, мое REST-приложение работало задолго до Ansyc, но после реализации нарушает модель персистентности JPA.
Вот случай:
Мой репозиторий имеет метод JpaRepository.save()
, но при вызове из отдельного потока он выдает InvalidDataAccessApiUsageException
ошибку.
Мой Контроллер вызывает Сервис , который вызывает Репозиторий для вставки нового объекта, и я получаю следующую ошибку:
InvalidDataAccessApiUsageException: detached entity passed to persist: TransactionalEntity; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: TransactionalEntity]
CONTROLLER:
@Autowired
@Qualifier("depositIntentService")
private TransactionIntentService depositIntentService;
@PostMapping("/services/transactions/deposit")
public CompletableFuture<ResponseEntity<TransactionIntent>> deposit(@Valid @RequestBody TransactionClientRequest request) {
CompletableFuture<TransactionIntent> depositIntentFuture =
transactionIntentFactory.createDepositIntent(
request.entity.id,
Money.of(CurrencyUnit.of(request.money.currency), request.money.amount));
return depositIntentFuture.thenApply(intent -> {
TransactionIntent publishedIntent = depositIntentService.attemptPublish(intent); //<-- causes error
ResponseEntity.ok(publishedIntent)
});
}
SERVICE:
@Component
@Repository
public abstract class TransactionIntentServiceImpl implements TransactionIntentService{
@Autowired
private TransactionIntentRepository transactionIntentRepo;
@Transactional
public TransactionIntent attemptPublish(TransactionIntent intent){
transactionIntentRepo.save(intent); //<-- Throws error: ...detached entity passed to persist
}
}
СКЛАД
@Repository
public interface TransactionIntentRepository extends JpaRepository<TransactionIntent, Long>{
}
Есть идеи, как поддерживать устойчивость JPA в среде Async?
Спасибо!
Update1
ФАБРИКА
@Component
public class TransactionIntentFactory {
@Autowired
private UserService userService;
@Async("asyncExecutor")
public CompletableFuture<TransactionIntent> createDepositIntent(long beneficiaryId, Money money) {
CompletableFuture<User> bank = userService.findByUsername("bankItself@bank.com");
CompletableFuture<User> user = userService.find(beneficiaryId);
CompletableFuture<Void> allUserFutures = CompletableFuture.allOf(bank, user);
return allUserFutures.thenApply(it -> {
User userSource = bank.join();
User userBeneficiary = user.join();
TransactionIntent intent = new TransactionIntentBuilder()
.status(new TransactionIntentStatus(TRANSFER_STATUS.CREATED, "Deposit"))
.beneficiary(userBeneficiary)
.source(userSource)
.amount(money)
.build();
return intent;
});
}
}
ЛИЦО
@Entity
public class TransactionIntent {
@Id
@GeneratedValue
public long id;
public final Money amount;
public final Date createdAt;
@OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionIntentStatus status;
@OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity beneficiary; //to
@OneToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
public final TransactionalEntity source; //from
TransactionIntent(){
this.amount= null;
this.createdAt = null;
this.status = null;
this.beneficiary = null;
this.source = null;
}
public TransactionIntent(TransactionIntentBuilder builder) {
this.amount = builder.amount;
this.createdAt = new Date();
this.status = builder.status;
this.beneficiary = builder.beneficiary;
this.source = builder.source;
}
}