批量更新后的JPA JQL查询看到过时的值

批量更新后的JPA JQL查询看到过时的值,jpa,caching,refresh,eclipselink,jpql,Jpa,Caching,Refresh,Eclipselink,Jpql,我有一个EclipseLink JPA演示应用程序,它在执行JPQL UPDATE语句后发出JPQL SELECT语句。 SELECT语句“查看”过时数据,如下所示: 如果未提供任何提示,则结果数据已过时 如果使用.setHint(“javax.persistence.cache.storeMode”、“REFRESH”),则检索更新的数据 如果.setHint(“javax.persistence.cache.retrieveMode”,“旁路”)过时数据被检索 如果我在查询之前执行em.c

我有一个EclipseLink JPA演示应用程序,它在执行JPQL UPDATE语句后发出JPQL SELECT语句。 SELECT语句“查看”过时数据,如下所示:

  • 如果未提供任何提示,则结果数据已过时
  • 如果使用.setHint(“javax.persistence.cache.storeMode”、“REFRESH”),则检索更新的数据
  • 如果.setHint(“javax.persistence.cache.retrieveMode”,“旁路”)过时数据被检索
  • 如果我在查询之前执行em.clear(),查询将检索更新后的值(但这是显而易见的,我想知道在未清除持久性上下文时会发生什么情况)。然而,这指向一个1级缓存问题
我无法理解JPQL SELECT从何处获取陈旧数据。它们显然不在共享缓存中(通过使用缓存接口的旁路提示和contains()方法确认)

我做的实验:

  • 将where 1=1添加到陈旧检索查询中,以检查是否发生了某些查询代码缓存(无更改)
  • 删除第二个SELECT查询中的提示,以检查可能延迟的数据库提交是否存在时间问题(第二个查询也会看到过时的数据)
有什么想法吗

package examples.client;
导入示例.model.Employee;
导入java.util.List;
导入javax.persistence.EntityManager;
导入javax.persistence.EntityManagerFactory;
导入javax.persistence.persistence;
公共类EmployeeUPDATEModification{
公共静态void main(字符串[]args){
EntityManagerFactory emf=Persistence.createEntityManagerFactory(“EmployeeService”);
EntityManager em=emf.createEntityManager();
System.out.println(“***初始薪资值\n”);
List initial=em.createQuery(“从Employee e中选择e”,Employee.class).getResultList();
对于(员工e:首字母){
System.out.println(e.getSalary());
}
System.out.println(“***测试批量更新\n”);
em.getTransaction().begin();
em.createQuery(“更新员工e设置e.salary=e.salary*2”).executeUpdate();
em.getTransaction().commit();
System.out.println(“***从初始列表批量更新后的薪资值\n”);
对于(员工e:首字母){
System.out.println(e.getSalary());
}
System.out.println(“\n***在没有提示的情况下从查询打印工资\n”);
List result=em.createQuery(“从Employee e中选择e”,Employee.class).getResultList();
对于(员工e:结果){
System.out.println(e.getSalary());
}
System.out.println(“\n***检查共享缓存\n”);
对于(员工e:结果){
System.out.println(emf.getCache().contains(Employee.class,e.getId())?e+“在共享chache中”
:e+“不在共享缓存中”);
}
System.out.println(“\n***从带有提示的查询打印工资\n”);
List result3=em.createQuery(“从Employee e中选择e”,Employee.class)
.setHint(“javax.persistence.cache.storeMode”,“刷新”).getResultList();
对于(员工e:result3){
System.out.println(e.getSalary());
}
//完成后关闭EM和EMF
em.close();
emf.close();
}
}
控制台

***INITIAL SALARY VALUES

[EL Fine]: sql: 2020-10-20 17:21:25.213--ServerSession(2092769598)--Connection(110651474)--Thread(Thread[main,5,main])--SELECT ID, NAME, SALARY FROM EMPLOYEE

100
200
300

***TESTING BULK UPDATE

[EL Fine]: sql: 2020-10-20 17:21:25.241--ClientSession(706665172)--Connection(110651474)--Thread(Thread[main,5,main])--UPDATE EMPLOYEE SET SALARY = (SALARY * ?)
    bind => [2]

***SALARY VALUES AFTER BULK UPDATE FROM INITIAL LIST

100
200
300

***PRINTING THE SALARY FROM A QUERY WITHOUT HINT 

[EL Fine]: sql: 2020-10-20 17:21:25.256--ServerSession(2092769598)--Connection(110651474)--Thread(Thread[main,5,main])--SELECT ID, NAME, SALARY FROM EMPLOYEE

100
200
300

***CHECKING THE SHARED CACHE

Employee id: 1 name: Piero salary: 100 is NOT in shared cache

Employee id: 2 name: Aldo salary: 200 is NOT in shared cache

Employee id: 3 name: Mario salary: 300 is NOT in shared cache

***PRINTING THE SALARY FROM A QUERY WITH HINT 

[EL Fine]: sql: 2020-10-20 17:21:25.271--ServerSession(2092769598)--Connection(110651474)--Thread(Thread[main,5,main])--SELECT ID, NAME, SALARY FROM EMPLOYEE

200
400
600
具有最佳日志记录级别的控制台

***PRINTING THE SALARY FROM A QUERY WITHOUT HINT 

[EL Finest]: query: 2020-10-20 18:14:08.097--UnitOfWork(955611965)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(referenceClass=Employee sql="SELECT ID, NAME, SALARY FROM EMPLOYEE")

[EL Finest]: connection: 2020-10-20 18:14:08.097--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--Connection acquired from connection pool [default].

[EL Fine]: sql: 2020-10-20 18:14:08.097--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--SELECT ID, NAME, SALARY FROM EMPLOYEE

[EL Finest]: connection: 2020-10-20 18:14:08.099--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--Connection released to connection pool [default].

400
800
1200

PRINTING THE SALARY FROM A QUERY WITH HINT 

***

[EL Finest]: query: 2020-10-20 18:14:08.119--UnitOfWork(955611965)--Thread(Thread[main,5,main])--Execute query ReadAllQuery(referenceClass=Employee sql="SELECT ID, NAME, SALARY FROM EMPLOYEE")

[EL Finest]: connection: 2020-10-20 18:14:08.119--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--Connection acquired from connection pool [default].

[EL Fine]: sql: 2020-10-20 18:14:08.119--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--SELECT ID, NAME, SALARY FROM EMPLOYEE

[EL Finest]: connection: 2020-10-20 18:14:08.121--ServerSession(1006485584)--Connection(1482246673)--Thread(Thread[main,5,main])--Connection released to connection pool [default].

[EL Finest]: transaction: 2020-10-20 18:14:08.122--UnitOfWork(955611965)--Thread(Thread[main,5,main])--Merge clone Employee id: 1 name: Piero salary: 800 

[EL Finest]: transaction: 2020-10-20 18:14:08.122--UnitOfWork(955611965)--Thread(Thread[main,5,main])--Merge clone Employee id: 2 name: Aldo salary: 1600 

[EL Finest]: transaction: 2020-10-20 18:14:08.123--UnitOfWork(955611965)--Thread(Thread[main,5,main])--Merge clone Employee id: 3 name: Mario salary: 2400 

800
1600
2400

您发布的答案提到了您的问题-您有一个从本地上下文读取的实体,并且在模型对象(您的批量更新查询)之外的更新中使其过时。每次您从EntityManager读取数据时,都会返回相同的陈旧员工实例数据—JPA要求它维护对象标识,而且由于您可能会在其中有未提交的更改,所以不能将其删除。因此,它将执行一个完整的列表操作,但当它看到一个它已经缓存/管理的Emp id时,只会按原样返回该实例

JPA规范规定,批量更新和删除更改的方式可能在上下文中不可见: 第4.10节: “持久性上下文与批量更新或删除的结果不同步。 执行批量更新或删除操作时应谨慎,因为它们可能会导致数据库与活动持久性上下文中的实体之间的不一致。通常,批量更新和删除操作只能在新持久性上下文中的事务内执行,或者在获取或访问实体之前执行其状态可能会受到此类操作的影响。”


您可以通过强制刷新以后的查询、清除实体管理器或在提交事务后获取新的实体管理器来解决此问题。然后,所有读取都将使用数据库中的数据。

您发布的答案提到了您的问题-您有一个从本地上下文读取的实体,并且由于模型对象(批量更新查询)之外的更新而使其过时。每次您从EntityManager读取数据时,都会返回相同的陈旧员工实例数据—JPA要求它维护对象标识,而且由于您可能会在其中有未提交的更改,所以不能将其删除。因此,它将执行一个完整的列表操作,但当它看到一个它已经缓存/管理的Emp id时,只会按原样返回该实例

JPA规范规定,批量更新和删除更改的方式可能在上下文中不可见: 第4.10节: “持久性上下文与批量更新或删除的结果不同步。 在执行批量更新或删除操作时应小心,因为它们可能会导致数据库和