esc*_*380 6 java spring spring-transactions spring-data-jpa spring-boot
对于这个基于spring-boot-starter-data-jpa依赖和H2内存数据库的实验项目,我定义了一个User具有两个字段(id和firstName)的实体,并UsersRepository通过扩展CrudRepository接口声明了一个。
现在,考虑一个简单的控制器,它提供两个端点:/print-user读取同一用户两次,间隔打印其名字,并/update-user用于在两次读取之间更改用户的名字。请注意,我特意设置了Isolation.READ_COMMITTEDlevel 并期望在第一次事务过程中,通过相同 id 检索两次的用户将具有不同的名称。但是相反,第一笔交易打印出两次相同的值。为了更清楚,这是完整的操作序列:
jeremy的名字设置为Jeremy。/print-userwhich 打印出来Jeremy并进入睡眠状态。/update-user从另一个会话调用,它将jeremy的名字更改为Bob.jeremy用户时,Jeremy即使名字已经更改为他的名字,它也会再次打印出来Bob(如果我们打开数据库控制台,它现在确实存储为Bob,不是Jeremy)。似乎在这里设置隔离级别没有任何影响,我很好奇为什么会这样。
@RestController
@RequestMapping
public class UsersController {
private final UsersRepository usersRepository;
@Autowired
public UsersController(UsersRepository usersRepository) {
this.usersRepository = usersRepository;
}
@GetMapping("/print-user")
@ResponseStatus(HttpStatus.OK)
@Transactional (isolation = Isolation.READ_COMMITTED)
public void printName() throws InterruptedException {
User user1 = usersRepository.findById("jeremy");
System.out.println(user1.getFirstName());
// allow changing user's name from another
// session by calling /update-user endpoint
Thread.sleep(5000);
User user2 = usersRepository.findById("jeremy");
System.out.println(user2.getFirstName());
}
@GetMapping("/update-user")
@ResponseStatus(HttpStatus.OK)
@Transactional(isolation = Isolation.READ_COMMITTED)
public User changeName() {
User user = usersRepository.findById("jeremy");
user.setFirstName("Bob");
return user;
}
}
Run Code Online (Sandbox Code Playgroud)
您的代码有两个问题。
您usersRepository.findById("jeremy");在同一个事务中执行两次,您的第二次读取很可能是从Cache. 第二次读取记录时需要刷新缓存。我已经更新了使用的代码entityManager,请检查如何使用JpaRepository
User user1 = usersRepository.findById("jeremy");
Thread.sleep(5000);
entityManager.refresh(user1);
User user2 = usersRepository.findById("jeremy");
Run Code Online (Sandbox Code Playgroud)
以下是我的测试用例的日志,请检查 SQL 查询:
休眠:选择 person0_.id 作为 id1_0_0_,person0_.city 作为 city2_0_0_,person0_.name 作为 name3_0_0_ from person person0_ where person0_.id=?
休眠:选择 person0_.id 作为 id1_0_0_,person0_.city 作为 city2_0_0_,person0_.name 作为 name3_0_0_ from person person0_ where person0_.id=?
Hibernate:更新人集 city=?, name=? 哪里 id=?
第二个可能的问题是/update-user端点处理程序逻辑。您正在更改用户名但没有将其保留回来,仅调用 setter 方法不会更新数据库。因此,当其他端点Thread唤醒时,它会打印 Jeremy。
因此,您需要userRepository.saveAndFlush(user)在更改名称后调用。
@GetMapping("/update-user")
@ResponseStatus(HttpStatus.OK)
@Transactional(isolation = Isolation.READ_COMMITTED)
public User changeName() {
User user = usersRepository.findById("jeremy");
user.setFirstName("Bob");
userRepository.saveAndFlush(user); // call saveAndFlush
return user;
}
Run Code Online (Sandbox Code Playgroud)
此外,您需要检查数据库是否支持所需的隔离级别。您可以参考H2 事务隔离级别
| 归档时间: |
|
| 查看次数: |
950 次 |
| 最近记录: |