Spring Async прерывает JPA - отдельная сущность передана для сохранения - PullRequest
0 голосов
/ 12 ноября 2018

Используя 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;
    }
}
...