我有简单的User
和Account
实体,它们看起来像:
@Entity
@Table(name="USERS")
public class User {
@NotNull
@Column(name = "USERNAME",length = 50, unique = true)
private String username;
@NotNull
@Column(name = "PASSWORD")
private String password;
@NotNull
@Column(name = "ROLE",length = 20)
@Enumerated(EnumType.STRING)
private Role role;
}
还有:
@Entity
@Table(name = "ACCOUNT")
public class Account {
@NotNull
@Column(name = "BALANCE")
private BigDecimal balance;
@JoinColumn(name = "USER_ID")
@OneToOne(targetEntity = User.class, fetch = FetchType.LAZY)
private User user;
@Version
private int version;
}
所以我试着写一个@Test
来确定它,它就像这样:
@Test
public void test_optimistic_locking_concept() {
User user = new User("test", "123456", Role.ROLE_USER);
user = userRepository.save(user);
Account account = new Account();
account.setBalance(new BigDecimal("5000"));
account.setUser(user);
accountRepository.save(account);
// fetching account record for different devices
Account accountInDeviceOne = new Account();
accountInDeviceOne = accountRepository.findAccountByUser_Username(user.getUsername()).get();
Account accountInDeviceTwo = new Account();
accountInDeviceTwo = accountRepository.findAccountByUser_Username(user.getUsername()).get();
// each device tries to change the account balance by debit/credit
accountInDeviceOne.setBalance(accountInDeviceOne.getBalance().subtract(new BigDecimal("1500")));
accountInDeviceTwo.setBalance(accountInDeviceTwo.getBalance().add(new BigDecimal("2500")));
// The versions of the updated accounts are both 0.
Assertions.assertEquals(0, accountInDeviceOne.getVersion());
Assertions.assertEquals(0, accountInDeviceTwo.getVersion());
// first device request update
accountInDeviceOne = accountRepository.save(accountInDeviceOne);
// !Exception!
accountInDeviceTwo = accountRepository.save(accountInDeviceTwo);
}
但是它没有抛出异常,正如我所期望的!!
此外,当我执行accountRepository.save(accountInDeviceOne)
时,它不会递增version
字段。
而在调试器控制台中,如下图所示,我不知道为什么它们都指向同一个资源!!!
有人能帮助我理解这里出了什么问题吗?我如何为这个乐观的锁定概念编写测试?
任何帮助都将不胜感激!
3条答案
按热度按时间nukf8bse1#
您只需在两个并发线程中更新一行,如下所示:
}
请注意,第一个runnable将在从数据库中检索对象6秒后尝试更新获取的对象,在此之前,该对象的版本字段将由其他线程(t2)更新,这将在t1导致OptimisticLock(Hibernate的StaleObjectStateException)异常。
brc7rcf02#
单个会话
一级高速缓存:Hibernate一级高速缓存与会话对象相关联。默认情况下,Hibernate一级高速缓存处于启用状态,无法将其禁用。但是,Hibernate提供了一些方法,通过这些方法,我们可以该高速缓存中删除选定的对象或完全清除高速缓存。在会话中高速缓存的任何对象对于其他会话都不可见,并且当会话关闭时,所有高速缓存的对象也将丢失。
因此,当您特灵通过
accountRepository.findAccountByUser_Username
hib从缓存中找到db对象时,就是这样。mefy6pfw3#
这是因为JPA中的自然行为。因为Hibernate实现了JPA,所以它使用缓存,而且它还有持久性上下文。当对象不在持久性上下文中时,使用Hibernate从数据库中调用(实体)(PC)它从DB加载并放入PC,然后作为调用结果返回。因此,当您从其他位置或以相同的方法再次加载它时,它将从PC返回相同的对象。而且,当您更新对象时,它将更新PC中的相同对象。但是,只有在提交事务时,对对象所做的更改才会刷新到DB。在您的示例中,您将两次从DB获取相同的对象。因此,第一次将对象从DB放入PC并返回,第二次将从PC返回相同的对象,并且不调用DB。您是否具有accountInDeviceOne和accountInDeviceTwo并不重要,因为它们都只是持久上下文(PC0)中对相同对象的引用。因此,它不无论您对哪个引用进行更改,您都是在更改同一个对象。此外,当提交事务时,版本会递增,更改会刷新到DB中。并且,在创建事务的方法完成后,提交事务。如果您希望版本立即递增,则可以通过调用实体管理器中的flush方法或在您使用的SpringDataJPA也有同样的可能性,但这并不是通常的做法。