Spring数据-“错误:当前事务中止”:捕获DataIntegrityViolationException时如何回滚/提交事务?

时间:2018-10-29 17:29:51

标签: java spring-boot transactions spring-data-jpa

如果我有一些实例要在数据库中插入或更新,例如:

@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@EqualsAndHashCode
@Table(name="user_purchases")
public class UserPurchase implements Serializable, Persistable<String> {
    @Id
    @Column(name="id")
    @NotNull
    private String id; // an UUID

    @Column(name="user_id")
    @NotNull
    private String userId; // in user_info: "sub"

    /**
     * Seconds since epoch: when the last purchase happened.
     */
    @Column(name="last_date")
    private Date lastBuyDate;

    @Transient
    private boolean isNewObject;


    // Add UUID before persisting
    @PrePersist
    public void prepareForInsert() {
        this.id = UUID.randomUUID().toString();
    }


    @Override
    public boolean isNew() {
        return isNewObject;
    }

    // This part for Persistable is not required, because getId(), by accident,
    // is the getter for the "id" field and returns a String.
    //@Override
    //public getId() {
    //    return id;
    //}
}

我们知道id是代理ID,将在持久化之前生成。 userId在数据库中是唯一的。

要了解有关界面Persistable<ID>的更多信息,请选中this answer

现在,当我们有一个不带id的实例时,userId可能会在数据库中复制或无法复制,并且无法判断我们是否在数据库中持久化或更新。

我想在每一个持久/更新之前保存全表扫描,因此我尝试在DateIntegrationViolationException的第一次尝试之后捕获repository.save(entity)

@Transactional(rollbackFor = DataIntegrityViolationException.class)
public UserPurchase saveUserPurchase(UserPurchase purchase) throws RuntimeException {
    UserPurchase saved = null;
    try {
        saved = repository.saveAndFlush(purchase);
        log.debug("UserPurchase was saved/updated with id {}, last buy time: {}", saved.getId(),
                DateTimeUtil.formatDateWithMilliPart(saved.getLastBuyDate(), false));
    } catch (DataIntegrityViolationException e) {
        log.info("Cannot save due to duplication. Rolling back..."); // we don't distinguish userId and id duplication here.
        UserPurchase oldPurchase = repository.findByUserId(purchase.getUserId());  // <--------- here we cannot proceed
        if (oldPurchase != null) {
            purchase.setId(oldPurchase.getId()); // set the existent ID to do updating
            purchase.setNewObject(false);  // for Persistable<String>
            saved = repository.saveAndFlush(purchase); // now should be updating

        } else {
            log.error("Cannot find ID by user id");
        }
    }
    return saved;
}

这给了我以下错误:

ERROR: current transaction is aborted, commands ignored until end of transaction 

因为我在一项事务中做了两件事,所以事务应该回滚。

好的,所以我抛出了异常,并尝试执行外部更新操作(因为当Spring看到抛出异常时,它会自动回滚,否则我会读):

@Transactional(rollbackFor = DataIntegrityViolationException.class)
public UserPurchase saveUserPurchase(UserPurchase purchase) throws RuntimeException {
    UserPurchase saved = null;
    try {
        saved = repository.saveAndFlush(purchase);
        log.debug("UserPurchase was saved/updated with id {}, last buy time: {}", saved.getId(),
                DateTimeUtil.formatDateWithMilliPart(saved.getLastBuyDate(), false));
    } catch (DataIntegrityViolationException e) {
        log.info("Cannot save due to duplication. Rolling back..."); // we don't distinguish userId and id duplication here.
        throw e; // or throw new RuntimeException(e); is the same
    }
    return saved;
}

在我打电话给save()的地方:

try {
    userPurchaseService.saveUserPurchase(purchase);
} catch (DataIntegrityViolationException e) {
    log.info("Transaction rolled back, updating...");
    // ... 1. select 2. getId() 3.setId() and save again
}

但是,它再次失败。

现在,对于Spring Data,我们没有EntityManagerrollback()

现在该怎么办?每次插入/更新之前,我都必须做一本手册findByUserId()吗?我的惰性选择方法在任何情况下都行不通?

0 个答案:

没有答案