我有一个EclipseLink JPA演示应用程序,它在执行一个JPQL UPDATE语句后发出一个JPQL SELECT语句。SELECT语句“看到”陈旧数据,如下所示:
我不明白 JPQL SELECT 从哪里获取过时的数据。它们显然不在共享缓存中(通过使用缓存接口的 BYPASS 提示和 contains() 方法确认)。
我做的实验:
知道吗?
package examples.client;
import examples.model.Employee;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
public class EmployeeUPDATEModification {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeeService");
EntityManager em = emf.createEntityManager();
System.out.println("***INITIAL SALARY VALUES\n");
List<Employee> initial = em.createQuery("SELECT e FROM Employee e", Employee.class).getResultList();
for (Employee e : initial) {
System.out.println(e.getSalary());
}
System.out.println("***TESTING BULK UPDATE\n");
em.getTransaction().begin();
em.createQuery("UPDATE Employee e SET e.salary = e.salary*2").executeUpdate();
em.getTransaction().commit();
System.out.println("***SALARY VALUES AFTER BULK UPDATE FROM INITIAL LIST\n");
for (Employee e : initial) {
System.out.println(e.getSalary());
}
System.out.println("\n***PRINTING THE SALARY FROM A QUERY WITHOUT HINT \n");
List<Employee> result = em.createQuery("SELECT e FROM Employee e", Employee.class).getResultList();
for (Employee e : result) {
System.out.println(e.getSalary());
}
System.out.println("\n***CHECKING THE SHARED CACHE\n");
for (Employee e : result) {
System.out.println(emf.getCache().contains(Employee.class, e.getId()) ? e + " is in shared chache"
: e + " is NOT in shared cache");
}
System.out.println("\n***PRINTING THE SALARY FROM A QUERY WITH HINT \n");
List<Employee> result3 = em.createQuery("SELECT e FROM Employee e", Employee.class)
.setHint("javax.persistence.cache.storeMode", "REFRESH").getResultList();
for (Employee e : result3) {
System.out.println(e.getSalary());
}
// close the EM and EMF when done
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节:“持久性上下文与大容量更新或删除的结果不同步。执行大容量更新和删除操作时应小心,因为这些操作可能会导致数据库与活动持久性上下文中的实体之间不一致。通常,大批量更新和删除操作应仅在新持久性上下文的事务中执行或者在获取或访问其状态可能受到此类操作影响的实体之前。"
您可以通过强制刷新以后的查询、清除实体管理器或在提交事务后获取新的实体管理器来解决此问题。然后,所有读取都将使用数据库中的数据。