首页 > 解决方案 > 如何避免覆盖实体中的数据

问题描述

我确实对 jpa 如何工作以及如何避免困扰我的一种情况有疑问。

好的,让我从简单的片段开始。

    @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. 我确实employee1从 db 获得,将其传递给ovewrite刚刚更改的方法,firstName之后我确实从 db 获得了同一个员工。

我在这里不明白的是,即使没有对数据库进行此更改的提交,employee3 也会更改名称。我想 jpa 正在缓存这些数据,并且对于这个对象的每个新find对象,我们都会得到修改的记录?

还有一个问题,我怎样才能避免这种情况?因为我确实有一个带有这种“错误”的项目。对我来说,解决这个问题的最简单方法就是禁止这个同步化的孩子。

[编辑] 所以我试图通过find在新事务内部调用来避免这个缓存“问题”。但是,我的employee3 仍然更改了名称属性。

@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();
    }

标签: javaspringspring-bootjpa

解决方案


实际上emplyee1 和employee3 将是同一个对象,这与JPA 缓存有关。所有 JPA 实现都至少具有 1 级缓存,即会话/事务缓存。您可以通过以下方式确认:

System.out.println(System.identityHashCode(employee1));
System.out.println(System.identityHashCode(employee3));

请注意,findById(1L) 返回的对象仅对于当前 JPA 事务是“相同的”。如果同时从另一个线程并行执行相同的 findById(1L),则 findById(1L) 的输出将是不同的对象。如果由于某种原因您从注释的方法中调用 findById(1L) 也是如此(您将收到不同的对象)

@javax.transaction.Transactional(REQUIRES_NEW)

或通过

@org.springframework.transaction.annotation.Transactional(propagation = Propagation = REQUIRES_NEW)

如果您想在修改employee1 对象时避免更改数据库,您可以将其从会话中分离,如果这是您真正需要的。分离的对象稍后可以重新连接,但有一些警告,很可能有更好的解决方法。


推荐阅读