Spring事务传播和乐观锁问题

4

我有一个设置,其中外部方法调用内部方法。可能会发生这种情况:内部方法将抛出异常,导致回滚。我不希望此异常影响外部方法。为了实现这一点,我在内部方法上使用了@Transactional(propagation=Propagation.REQUIRED_NEW)。

以下是我的代码的简化版本:

public class ServiceAImpl implements ServiceA{
    @Autowired
    private ServiceB serviceB;

    @Transactional(propagation=Propagation.REQUIRED)
    public void updateParent(Parent parent) {
        update(parent);
        serviceB.updateChild(parent);
    }
}

public class ServiceBImpl implements ServiceB {
    @Transactional(propagation=Propagation.REQUIRED_NEW)
    public void updateChild(Parent parent) {
        checkIfChildHasErrors(parent.getChild()); //throws RuntimeException if Child has errors
        update(parent.getChild());
    }
}

public class Parent {
    @Version
    private Integer version;
    private Child child;

    //insert getters and setters
}

public class Child {
    @Version
    private Integer version;

    //insert getters and setters
}

我对传播(Propagation)还不是很熟悉,但根据我的理解,由于外部方法(updateParent)使用了Propagation.REQUIRED,而内部方法(updateChild)使用了Propagation.REQUIRED_NEW,它们现在将分别运行在自己的事务中。如果内部方法遇到异常,它将会回滚,但不会引起外部方法的回滚。

当外部方法运行时,它调用内部方法。外部方法被暂停,而内部方法被执行。一旦内部方法完成,由于它是一个不同的事务,所以它会被提交。然后外部方法继续执行并也会被提交为另一个事务。

我遇到的问题是:提交外部方法的过程会触发Child类的乐观锁定(可能是因为版本字段的值在内部方法结束并提交后已经改变)。由于外部方法的Child实例已经过期,提交它将触发乐观锁定。

我的问题是: 有没有一种方法可以防止外部方法触发乐观锁定?

我很惊讶外部方法竟然尝试提交对Child类的更改。我原以为由于内部方法包含在自己的事务中,所以外部方法的事务将不再包括updateChild方法。

我使用的是Spring 3.0.5和Hibernate 3.6.10。


一个简单的解决方案是在外部方法中捕获内部方法的乐观锁异常,并将其传递。 - Esty
嗨,Tanjim,抛出乐观锁异常的是外部方法。内部方法在不抛出异常的情况下更新数据库记录。而且,我认为捕获并忽略乐观锁并不明智,如果通过并发用户更新引发了真正的乐观锁异常,那么我就完了。 - poach
1
您的传播是正确的。在外部事务上使用Propagation.REQUIRED,在内部事务上使用Propagation.REQUIRED_NEW,不会影响任何提交/回滚内部事务的外部事务。请确定在操作期间修改了哪些行。我认为您正在处理相同的行,版本字段正在更新并且抛出了OptimisticLockException异常。 - Esty
update(parent) 方法正在更新父级和 serviceB.updateChild(parent) 方法正在更新子级。实体 Parent 和 Child 正在更新同一行吗?我在这里有点困惑。 - Esty
是的,这对我来说也很困惑。为什么外部方法尝试更新相同的行?我的猜测是因为外部方法也调用了内部方法,因此外部方法的事务也包含了对Child类的更新。由于内部方法被视为单独的事务,内部和外部方法对update(parent.getChild())的调用被计为两次尝试更新相同的记录。我不知道这是否是正常行为。 - poach
显示剩余5条评论
1个回答

0

假设您正在使用合并进行更新

对于内部事务

Entity entityUpdated = entityManager.merge(entity);

对于外部事务

if (entityUpdated != null){
    // if inner transaction rolledback entityUpdated will be null. Condition will save u from nullPointerException
    outerEntity.setVersion(entityUpdated.getVersion);
}

entityManager.merge(outerEntity);

嗨,Tanjim,合并操作未能解决问题,不幸的是。尽管如此,我仍然感谢你的帮助。 - poach
绝对合并不是解决问题的关键。关键在于当您第二次更新行时,只需使用更新后的版本,这是避免OptimisticLockingException的唯一方法。 - Esty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接