为什么 JPA Entity select 结果在每个偶数查询上都会发生变化?
Why JPA Entity select result changes on each even query?
我的问题与奇怪的 read/select 行为有关,即每次调用后相同查询 returns 不同的结果。我的情况描述如下:
我有以下代码,从 DB
返回文档列表
@RequestMapping(value={"/docs"}, method = RequestMethod.GET)
@ResponseBody
public ArrayList<Document> getMetaData(ModelMap modelMap) {
return (ArrayList<Document>)documentDAO.getDocuments();
}
DocumentDAO.getDocuments 长得像
public List<Document> getDocuments() {
Query query = entityManager.createQuery("from Document");
List<Document> list = query.getResultList();
for(Document doc:list) System.out.println(doc.getName()+" "+doc.isSigned());
return list;
}
在其他控制器中,我也在提取文档并将布尔值 属性 更改为
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
documentDAO.updateDocument(doc); // IS IT NECESSARY??
getById 和 updateDocument 如下:
public Document getDocumentById(Long id) {
return entityManager.find(Document.class, id);
}
@Transactional
public void updateDocument(Document document) {
entityManager.merge(document);
entityManager.flush();
}
问题:
- 据我所知,设置托管对象的 属性 足以将更改传播到数据库。但是我想 flush 立即更改。我额外调用更新的方法是合适的解决方案还是调用 setter 足以立即更改数据库?通过额外更新,我的意思是
documentDAO.updateDocument(doc); // IS IT NECESSARY??
- JPA 如何存储托管对象 - 在某些内部数据结构中或只是将它们保存在
Document doc;
之类的引用中?内部结构很可能使 duplicate/sameID 托管对象成为不可能,引用很可能使具有相同 ID 和其他属性的多个托管对象成为可能。
merge
如何在内部工作 - 尝试在内部存储中查找具有相同 ID 的托管对象,并在检测到的情况下刷新其字段或 只是更新数据库?
- 如果内部存储真的存在(很可能这是持久性上下文,进一步的 PC),区分托管对象的标准是什么? @Id 注释 hibernate 模型字段?
我的主要问题是 entityManager.createQuery("from Document");
的不同结果
System.out.println(doc.getName()+" "+doc.isSigned());
在奇数调用上显示 isSigned true,在偶数调用上显示 false。
我怀疑首先 select-all-query returns 带有 isSigned=false 的实体并将它们放入 PC,然后用户执行一些操作,通过 ID 获取实体,设置 isSigned=true 和刚刚提取的实体与 PC 中已经存在的实体冲突。 第一个对象有 isSigned=false,第二个对象有 isSigned=true 和 PC 混淆和 returns 轮流管理的不同对象。 但这怎么可能呢?在我看来,PC 有一种机制可以通过为每个唯一 ID 只保留一个托管对象来防止出现这种令人困惑的模棱两可的情况。
首先,您想要在单个事务服务方法中同时注册读取和写入:
@Transactional
public void signDocument(Long id) {
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
}
所以这段代码应该驻留在服务端,而不是在您的网络控制器中。
- As far as I know, setting property of managed object is enough to propagate changes to DB. But I want to flush changes immediately. Is
my approach with extra call of update is appropriate solution or
calling setter is enough for making immediate changes in DB? By extra
update I mean documentDAO.updateDocument(doc); // IS IT NECESSARY??
这仅适用于托管实体,只要持久性上下文仍处于打开状态。这就是为什么您需要一种事务性服务方法。
- How JPA stores managed objects - in some internal data structure or simply keeps them in references like Document doc;? Internal structure
most likely makes duplicate/sameID managed object impossible,
references most likely makes possible to have multiple managed objects
with same id and other properties.
JPA 一级缓存只按原样存储实体,它不使用任何其他数据表示。在持久性上下文中,您可以有一个且只有一个实体表示(Class 和标识符)。在 JPA 持久性上下文的上下文中,托管 entity equality is the same with entity identity.
How merge works internally - tries to find managed object with the
same ID in internal storage and, in the case of detecting, refreshes
it's fields or simply updates DB?
merge
操作很有意义,因为 reattaching detached entities. A managed entity state is automatically synchronized with the database during flush-time. The automatic dirty checking mechanism 会处理这个问题。
- 如果内部存储真的存在(很可能这是持久化上下文,进一步PC),区分托管对象的标准是什么? @Id 注解休眠模型的字段?
PersistenceContext 是会话级缓存。托管对象始终具有标识符和关联的数据库行。
I suspect that first select-all-query returns entities with
isSigned=false and put them to PC, after that user performs some
operation which grabs entity byID, sets isSigned=true and just
extracted entity conflicts with already presented in PC.
在相同的 Persistence Context 范围内,这永远不会发生。如果您通过查询加载一个实体,该实体将在一级缓存中获得缓存。如果您尝试使用另一个查询或 EntityManager.find() 再次加载它,您仍将获得相同的对象引用,该引用已被缓存。
如果第一个查询针对持久性上下文发生,而第二个 query/find 将在第二个持久性上下文上发出,那么每个持久性上下文都必须缓存自己的被查询实体版本。
First object has isSigned=false, second has isSigned=true and PC
confused and returns different managed objects in rotation. But how
its possible?
这不可能发生。持久性上下文始终保持实体对象的完整性。
我的问题与奇怪的 read/select 行为有关,即每次调用后相同查询 returns 不同的结果。我的情况描述如下:
我有以下代码,从 DB
返回文档列表@RequestMapping(value={"/docs"}, method = RequestMethod.GET)
@ResponseBody
public ArrayList<Document> getMetaData(ModelMap modelMap) {
return (ArrayList<Document>)documentDAO.getDocuments();
}
DocumentDAO.getDocuments 长得像
public List<Document> getDocuments() {
Query query = entityManager.createQuery("from Document");
List<Document> list = query.getResultList();
for(Document doc:list) System.out.println(doc.getName()+" "+doc.isSigned());
return list;
}
在其他控制器中,我也在提取文档并将布尔值 属性 更改为
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
documentDAO.updateDocument(doc); // IS IT NECESSARY??
getById 和 updateDocument 如下:
public Document getDocumentById(Long id) {
return entityManager.find(Document.class, id);
}
@Transactional
public void updateDocument(Document document) {
entityManager.merge(document);
entityManager.flush();
}
问题:
- 据我所知,设置托管对象的 属性 足以将更改传播到数据库。但是我想 flush 立即更改。我额外调用更新的方法是合适的解决方案还是调用 setter 足以立即更改数据库?通过额外更新,我的意思是
documentDAO.updateDocument(doc); // IS IT NECESSARY??
- JPA 如何存储托管对象 - 在某些内部数据结构中或只是将它们保存在
Document doc;
之类的引用中?内部结构很可能使 duplicate/sameID 托管对象成为不可能,引用很可能使具有相同 ID 和其他属性的多个托管对象成为可能。 merge
如何在内部工作 - 尝试在内部存储中查找具有相同 ID 的托管对象,并在检测到的情况下刷新其字段或 只是更新数据库?- 如果内部存储真的存在(很可能这是持久性上下文,进一步的 PC),区分托管对象的标准是什么? @Id 注释 hibernate 模型字段?
我的主要问题是 entityManager.createQuery("from Document");
System.out.println(doc.getName()+" "+doc.isSigned());
在奇数调用上显示 isSigned true,在偶数调用上显示 false。
我怀疑首先 select-all-query returns 带有 isSigned=false 的实体并将它们放入 PC,然后用户执行一些操作,通过 ID 获取实体,设置 isSigned=true 和刚刚提取的实体与 PC 中已经存在的实体冲突。 第一个对象有 isSigned=false,第二个对象有 isSigned=true 和 PC 混淆和 returns 轮流管理的不同对象。 但这怎么可能呢?在我看来,PC 有一种机制可以通过为每个唯一 ID 只保留一个托管对象来防止出现这种令人困惑的模棱两可的情况。
首先,您想要在单个事务服务方法中同时注册读取和写入:
@Transactional
public void signDocument(Long id) {
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
}
所以这段代码应该驻留在服务端,而不是在您的网络控制器中。
- As far as I know, setting property of managed object is enough to propagate changes to DB. But I want to flush changes immediately. Is my approach with extra call of update is appropriate solution or calling setter is enough for making immediate changes in DB? By extra update I mean documentDAO.updateDocument(doc); // IS IT NECESSARY??
这仅适用于托管实体,只要持久性上下文仍处于打开状态。这就是为什么您需要一种事务性服务方法。
- How JPA stores managed objects - in some internal data structure or simply keeps them in references like Document doc;? Internal structure most likely makes duplicate/sameID managed object impossible, references most likely makes possible to have multiple managed objects with same id and other properties.
JPA 一级缓存只按原样存储实体,它不使用任何其他数据表示。在持久性上下文中,您可以有一个且只有一个实体表示(Class 和标识符)。在 JPA 持久性上下文的上下文中,托管 entity equality is the same with entity identity.
How merge works internally - tries to find managed object with the same ID in internal storage and, in the case of detecting, refreshes it's fields or simply updates DB?
merge
操作很有意义,因为 reattaching detached entities. A managed entity state is automatically synchronized with the database during flush-time. The automatic dirty checking mechanism 会处理这个问题。
- 如果内部存储真的存在(很可能这是持久化上下文,进一步PC),区分托管对象的标准是什么? @Id 注解休眠模型的字段?
PersistenceContext 是会话级缓存。托管对象始终具有标识符和关联的数据库行。
I suspect that first select-all-query returns entities with isSigned=false and put them to PC, after that user performs some operation which grabs entity byID, sets isSigned=true and just extracted entity conflicts with already presented in PC.
在相同的 Persistence Context 范围内,这永远不会发生。如果您通过查询加载一个实体,该实体将在一级缓存中获得缓存。如果您尝试使用另一个查询或 EntityManager.find() 再次加载它,您仍将获得相同的对象引用,该引用已被缓存。
如果第一个查询针对持久性上下文发生,而第二个 query/find 将在第二个持久性上下文上发出,那么每个持久性上下文都必须缓存自己的被查询实体版本。
First object has isSigned=false, second has isSigned=true and PC confused and returns different managed objects in rotation. But how its possible?
这不可能发生。持久性上下文始终保持实体对象的完整性。