【发布时间】:2021-08-08 12:14:19
【问题描述】:
我确实对 jpa 的工作方式有疑问,以及如何避免困扰我的情况。
好的,让我从简单的 sn-p 开始。
@GetMapping("/")
public List<Employee> index() {
Employee employee1 = employeeRepository.findById(1L).get();
Employee employee2 = ovewrite(employee1);
Employee employee3 = employeeRepository.findById(1L).get();
return Arrays.asList(employee1, employee2, employee3);
}
private Employee ovewrite(Employee employee){
employee.setFirstName("Test");
return employee;
}
如您所见,我创建了一些代码来测试使用id == 1 找到的员工会发生什么。
我确实从 db 获得了 employee1,将其传递给了 ovewrite 方法,该方法只是更改了 firstName,之后我确实从 db 获得了同一个员工。
我在这里不明白的是,即使没有对数据库进行此更改的提交,employee3 也会更改名称。我想 jpa 正在缓存这些数据,并且每个新的 find 到这个对象我们都会得到修改记录?
还有一个问题,我怎样才能避免这种情况?因为我确实有一个带有这种“错误”的项目。对我来说,解决这个问题的最简单方法就是禁止这个同步化的孩子。
[编辑]
所以我试图通过在新事务中调用find 来避免这个缓存“问题”。但是,我的 employee3 仍然更改了 name 属性。
@GetMapping("/")
public List<Employee> index() {
Employee employee1 = employeeRepository.findById(1L).get();
ovewrite(employee1);
Employee employee3 = newTxFind();
return Arrays.asList(employee1, employee3);
}
private Employee ovewrite(Employee employee){
employee.setFirstName("Test");
return employee;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Employee newTxFind(){
return employeeRepository.findById(1L).get();
}
【问题讨论】:
-
请阅读:Can I ask only one question per post? --- 方法
index是否有可能被@Transactional注释? -
@Turing85 不,你在我的代码中看到的 sn-p 是在粘贴在这里时运行的
-
那么它可能 - 实际上 - 是由于缓存。你能检查
employee2 == employee3的值吗? -
AFAIK Spring Boot 使用一个名为“Open Session In View”(OSIV)的概念。这将为控制器请求打开一个 Hibernate 会话,并在该请求完成后关闭它。因此,Hibernate 缓存很可能适用于两个调用
employeeRepository.findById(1L)。看看,如果禁用 OSIV (spring.jpa.open-in-view = false) 会改变行为。 (我不太确定......) -
@Seelenvirtuose 是对的,只是查了一下。
open-in-view默认激活(详见this article over at Baeldung)。这意味着从数据库中获取的实体仍然附加到事务上下文中。作为回报,这意味着对实体的更改会自动提交到数据库。为了防止这种行为,我们有两个通用选项:在另一个事务中执行获取或通过实体管理器显式分离实体。
标签: java spring spring-boot jpa