如何使用Spring和hibernate在数据库级实现竞争条件?

时间:2014-04-18 12:53:57

标签: java spring oracle hibernate race-condition

我有一个银行项目,客户余额应该由并行应用程序中的并行线程更新。我在Oracle数据库中保存客户余额。我的java应用程序将使用Spring和Hibernate实现。

如何在并行应用程序之间实现竞争条件?我的解决方案应该是数据库级别还是应用程序级别?

1 个答案:

答案 0 :(得分:1)

我假设你想知道的是如何处理并发,防止竞争条件,这种情况可能发生在应用程序的两个部分修改并意外覆盖相同数据的情况下。

你主要有两种策略:悲观锁定和乐观锁定:

悲观锁定

这里你假设两个线程覆盖相同数据的可能性很高,所以你希望它以透明的方式处理它。要处理此问题,请将Spring事务的隔离级别从其默认值READ_COMMITTED增加到例如REPEATABLE_READ,这在大多数情况下应该足够了:

@Transactional(isolation=Isolation.REPEATABLE_READ)
public void yourBusinessMethod {
    ...
}

在这种情况下,如果您在方法开头读取了一些数据,则可以确保在您的方法正在进行时,没有人可以覆盖数据库中的数据。请注意,另一个线程仍然可以将额外的记录插入到您所做的查询中(称为幻像读取的问题),但不能更改您已读过的记录。

如果要防止幻像读取,则需要将隔离级别升级到SERIALIZABLE。改进的隔离性能会带来性能损失,程序运行速度会变慢,并且会更频繁地“挂起”等待程序的其他部分完成。

乐观锁定

在这里,您假设数据访问权限很少,并且在极少数情况下,它们很容易被应用程序恢复。在此模式下,您可以将所有业务方法保持为默认的REPEATABLE_READ模式。

然后每个Hibernate实体都标有版本列:

@Entity
public SomeEntity {
    ...
    @Version
    private Long version;
}

这样,使用版本列对从数据库中读取的每个实体进行版本控制。当Hibernate将更改写入数据库中的实体时,它将检查自上次事务读取实体后该版本是否增加。

如果是这样,则意味着其他人修改了数据,并决定使用陈旧数据做出的决定。在这种情况下,抛出StaleObjectException,需要被应用程序捕获并处理,最好是在中心位置。

对于GUI,您通常会捕获异常,显示一条消息user xyz changed this data while you where also editing it, your changes are lost. Press Ok to reload the new data.

使用乐观锁定,您的程序将运行得更快,但应用程序需要处理一些并发方面,否则这些方面会因悲观锁定而变得透明:版本实体,捕获异常。

最常用的方法是乐观锁定,因为它似乎在大多数应用程序中都是可以接受的。使用悲观锁定很容易导致性能问题,特别是当数据访问权限很少并且可以通过简单的方式解决时。

如果需要,在同一个应用程序中混合使用两种并发处理方法没有约束。