JPA JPQL select 查询是从数据库还是从持久性上下文中读取?
Does JPA JPQL select query reads from database or from persistence context?
我有文档实体和 id=1 的文档的一些托管文档对象。
Document managedDoc = entityManager.find(Document .class, 1);
managedDoc.setName("changedName");
据我所知,在调用 setter 之后,持久上下文(进一步的 PC)中的托管文档状态发生了变化,但数据库中没有任何变化。在我的代码中的某处,我执行以下操作:
Query query = entityManager.createQuery("from Document");
List<Document> list = query.getResultList();
return list;
当我执行如上所示的select-all查询时,id=1的文档是从数据库中获取还是从PC中获取?从数据库中意味着select 将看不到新名称,因为新名称仍在 PC 中。
实际上,我的问题是通过 merge() and flush()
更新并进一步检索所有对象 - 目前我的 select-all 查询没有看到某些字段的新值。看起来 merge+flush 没问题,但是 JPA 查询不是从 DB 而是从 PC 读取的。但即使我是对的,PC 和 DB 都包含名称的新值,为什么我的 select-all 没有看到它?
此外,select全部
更新
澄清:
- 我通过
entityManager.find(Document .class, 1);
将一些对象放到 PC 上
- 我创建了新的分离实例,并设置了一些名称 属性。 Id 和其他道具从托管实例中获取。例如,
managedDoc = getFromSomeDataStructure();
Document nonManaged = new Document(managedDoc.getId()); nonManaged.setName("newName");
- 我通过
em.merge(nonManaged);flush();
更新数据库
- 我在 Workbench 中查看了数据库中的更改。
- 我正在按 F5(甚至 CTRL+F5)按钮执行 select-all JPQL 查询,在每个奇数按钮上按==select-all query 我看到非实际旧值,在每个偶数按钮上按下 ==select-所有查询我都看到正确的值。
它将从持久上下文中获取,只要它有它们。更正确地说:只要您有一个处于托管状态的实体(即在持久性上下文中),它就不会被覆盖。当然,在使用同一个 EntityManager 实例的上下文中。
如果你想从数据库中重新获取值,你有不同的可能性:
- 在不同的事务中使用另一个 EntityManager(重要!)。
- 使用
EntityManager.detach()
或者如果你想清除整个持久化上下文,使用EntityManager.clear()
- 使用
EntityManager.refresh()
丢弃对实体实例所做的所有更改。
让我试着用几个例子来澄清,也许这会回答你的问题,或者运气好的话,有助于使问题更清楚。
场景 #1:两个不同的读数
Department department = em.find(Department.class, 1);
department.setName("Jedi Masters");
TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
if(found.getId().equals(1)){
assert found == department;
assert found.getName().equals(department.getName());
}
}
在第一种情况下,您可以预期 department
和 found
是完全相同的实例,因此具有完全相同的值。以上两个断言都通过了。
场景 #2:合并分离的实体
//detached entity
Department department = new Department();
department.setId(1);
department.setName("Jedi Masters");
em.merge(department);
TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
if(found.getId().equals(1)){
assert found != department);
assert found.getName().equals(department.getName());
}
}
至少对于 Hibernate,这种情况下的行为略有不同。这两个对象不是同一个实例。它们是不同的实例,但它们仍然应该具有相同的内容。
因此,根据您比较它们的实施方式,您可能会得到意想不到的结果,最重要的是,如果您没有为这样的分离案例实施正确的 equals/hashCode 协议。
As answered here,我应该为结果列表中的每个项目调用 refresh()。但只有刷新对我不起作用。通过写入
在 persistence.xml 中设置 READ COMMITED 后
<property name="hibernate.connection.isolation" value="2" />
一切都很完美。
P.S 不要忘记将 select 方法标记为 @Transactional 因为没有此注释,refresh() 将无法工作。
我有文档实体和 id=1 的文档的一些托管文档对象。
Document managedDoc = entityManager.find(Document .class, 1);
managedDoc.setName("changedName");
据我所知,在调用 setter 之后,持久上下文(进一步的 PC)中的托管文档状态发生了变化,但数据库中没有任何变化。在我的代码中的某处,我执行以下操作:
Query query = entityManager.createQuery("from Document");
List<Document> list = query.getResultList();
return list;
当我执行如上所示的select-all查询时,id=1的文档是从数据库中获取还是从PC中获取?从数据库中意味着select 将看不到新名称,因为新名称仍在 PC 中。
实际上,我的问题是通过 merge() and flush()
更新并进一步检索所有对象 - 目前我的 select-all 查询没有看到某些字段的新值。看起来 merge+flush 没问题,但是 JPA 查询不是从 DB 而是从 PC 读取的。但即使我是对的,PC 和 DB 都包含名称的新值,为什么我的 select-all 没有看到它?
此外,select全部
更新
澄清:
- 我通过
entityManager.find(Document .class, 1);
将一些对象放到 PC 上
- 我创建了新的分离实例,并设置了一些名称 属性。 Id 和其他道具从托管实例中获取。例如,
managedDoc = getFromSomeDataStructure(); Document nonManaged = new Document(managedDoc.getId()); nonManaged.setName("newName");
- 我通过
em.merge(nonManaged);flush();
更新数据库
- 我在 Workbench 中查看了数据库中的更改。
- 我正在按 F5(甚至 CTRL+F5)按钮执行 select-all JPQL 查询,在每个奇数按钮上按==select-all query 我看到非实际旧值,在每个偶数按钮上按下 ==select-所有查询我都看到正确的值。
它将从持久上下文中获取,只要它有它们。更正确地说:只要您有一个处于托管状态的实体(即在持久性上下文中),它就不会被覆盖。当然,在使用同一个 EntityManager 实例的上下文中。
如果你想从数据库中重新获取值,你有不同的可能性:
- 在不同的事务中使用另一个 EntityManager(重要!)。
- 使用
EntityManager.detach()
或者如果你想清除整个持久化上下文,使用EntityManager.clear()
- 使用
EntityManager.refresh()
丢弃对实体实例所做的所有更改。
让我试着用几个例子来澄清,也许这会回答你的问题,或者运气好的话,有助于使问题更清楚。
场景 #1:两个不同的读数
Department department = em.find(Department.class, 1);
department.setName("Jedi Masters");
TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
if(found.getId().equals(1)){
assert found == department;
assert found.getName().equals(department.getName());
}
}
在第一种情况下,您可以预期 department
和 found
是完全相同的实例,因此具有完全相同的值。以上两个断言都通过了。
场景 #2:合并分离的实体
//detached entity
Department department = new Department();
department.setId(1);
department.setName("Jedi Masters");
em.merge(department);
TypedQuery<Department> typedQuery = em.createQuery("SELECT d FROM Department d", Department.class);
List<Department> departments = typedQuery.getResultList();
for(Department found : departments){
if(found.getId().equals(1)){
assert found != department);
assert found.getName().equals(department.getName());
}
}
至少对于 Hibernate,这种情况下的行为略有不同。这两个对象不是同一个实例。它们是不同的实例,但它们仍然应该具有相同的内容。
因此,根据您比较它们的实施方式,您可能会得到意想不到的结果,最重要的是,如果您没有为这样的分离案例实施正确的 equals/hashCode 协议。
As answered here,我应该为结果列表中的每个项目调用 refresh()。但只有刷新对我不起作用。通过写入
在 persistence.xml 中设置 READ COMMITED 后<property name="hibernate.connection.isolation" value="2" />
一切都很完美。
P.S 不要忘记将 select 方法标记为 @Transactional 因为没有此注释,refresh() 将无法工作。