2 回答
TA贡献1840条经验 获得超5个赞
乐观锁定确保在加载和保存实体之间没有对实体进行任何其他更改。由于您的服务在保存实体之前立即加载它,因此另一个线程不太可能在这个短时间范围内干扰,这就是为什么只有让线程休眠时才会看到冲突的原因。
如果您想将乐观锁定提供的保护扩展到数据库事务之外,您可以将先前加载的实体传递给客户端并返回,并保存它而无需再次加载:
public User updateUser(User user) {
return userRepository.save(user);
}
(这调用entityManager.merge(),它会自动检查版本)
或者,如果您需要更精细地控制更新哪些字段,您可以传递这些字段和版本,并在保存时自行检查版本:
public User updateUser(UserDto user) {
User savedUser = userRepository.findById(user.getId());
if (savedUser.getVersion() != user.getVersion()) {
throw new OptimisticLockingViolationException();
}
savedUser.setName(user.getName());
}
TA贡献1898条经验 获得超8个赞
您可以ExecutorService用来管理多线程并CyclicBarrier同步线程执行(或至少缩短线程之间的执行时间间隔)。
我做了一个打电话给你的UserService班级的例子:
存储库
public interface UserRepository extends CrudRepository<User, Long> {
@Lock(value = LockModeType.OPTIMISTIC)
Optional<User> findById(Long id);
}
JUnit 测试用例
// Create a Callable that updates the user
public Callable<Boolean> createCallable(User user, int tNumber, CyclicBarrier gate) throws OptimisticLockingFailureException {
return () -> {
// Create POJO to update, we add a number to string fields
User newUser = new User(user.getId(),
user.getFistName() + "[" + tNumber + "]",
user.getLastName() + "[" + tNumber + "]",
user.getEmail());
// Hold on until all threads have created POJO
gate.await();
// Once CyclicBarrier is open, we run update
User updatedUser = userService.updateUser(newUser);
return true;
};
}
@Test(expected = ObjectOptimisticLockingFailureException.class)
public void userServiceShouldThrowOptimisticLockException() throws Throwable {
final int threads = 2; // We need 2 threads
final CyclicBarrier gate = new CyclicBarrier(threads); // Barrier will open once 2 threads are awaiting
ExecutorService executor = Executors.newFixedThreadPool(threads);
// Create user for testing
User user = new User("Alfonso", "Cuaron", "alfonso@ac.com");
User savedUser = userRepository.save(user);
// Create N threads that calls to service
List<Callable<Boolean>> tasks = new ArrayList<>();
for(int i = 0; i < threads; i++) {
tasks.add(createCallable(savedUser, i, gate));
}
// Invoke N threads
List<Future<Boolean>> result = executor.invokeAll(tasks);
// Iterate over the execution results to browse exceptions
for (Future<Boolean> r : result) {
try {
Boolean temp = r.get();
System.out.println("returned " + temp);
} catch (ExecutionException e) {
// Re-throw the exception that ExecutorService catch
throw e.getCause();
}
}
}
我们使用Callable,因为它可以抛出Exceptions,我们可以从中恢复ExecutorService。
请注意,线程调用和保存语句之间的指令越多,它们不同步导致 OptimisticLockException 的可能性就越大。由于您将调用控制器,我建议增加线程数量以获得更好的机会。
添加回答
举报