我的问题与每次调用后相同查询返回不同结果时的奇怪读/选行为有关。我的情况描述如下:
我有以下代码,从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();
}
问题:
documentDAO.updateDocument(doc); // IS IT NECESSARY??
Document doc;
等引用中?内部结构很可能使重复/同一ID托管对象不可能,引用最有可能使多个托管对象具有相同的id和其他属性。merge
如何在内部工作 - 尝试在内部存储中找到具有相同 ID 的托管对象,并且在检测到的情况下,刷新其字段或只是更新数据库? 我的主要问题是entityManager.createQuery("from Document");
System.out.println(doc.getName()+" "+doc.isSigned());
在奇数次调用时显示isSigned,在偶数调用时显示为false。
我怀疑第一次select-all-query返回isSigned = false的实体并将它们放到PC上,之后用户执行一些操作来抓取实体byID,设置isSigned = true并且刚刚提取的实体冲突已经在PC中显示。 第一个对象的isSigned = false,第二个具有isSigned = true且PC混淆并返回不同的托管对象。 但它是如何可能的?在我看来,PC有机制通过为每个唯一ID只保留一个托管对象来不允许这种令人困惑的暧昧情境。
答案 0 :(得分:1)
首先,您希望在单个事务服务方法中注册读取和写入:
@Transactional
public void signDocument(Long id) {
Document doc = documentDAO.getDocumentById(id)
doc.setSigned(true);
}
因此,此代码应位于服务端,而不是Web控制器中。
- 据我所知,设置托管对象的属性足以将更改传播到DB。但是我想立即更新。是 我额外调用更新的方法是适当的解决方案或 调用setter足以在DB中立即进行更改?额外的 更新我的意思是documentDAO.updateDocument(doc); //是否必要?
醇>
这仅适用于托管实体,只要持久性上下文仍处于打开状态即可。这就是你需要一种交易服务方法的原因。
- JPA如何存储托管对象 - 在某些内部数据结构中,或者只是将它们保存在像Document doc这样的引用中;内部结构 最有可能使重复/ sameID托管对象不可能, 引用最有可能使多个托管对象成为可能 具有相同的id和其他属性。
醇>
JPA第1级缓存只是按原样存储实体,它不使用任何其他数据表示。在持久化上下文中,您可以拥有一个且仅有一个实体表示(类和标识符)。在JPA持久化上下文的上下文中,托管entity equality is the same with entity identity。
内部如何合并 - 尝试使用 内部存储中的相同ID,以及在检测时刷新的ID 它是字段还是只是更新DB?
merge
操作对reattaching detached entities有意义。在刷新时,管理实体状态会自动与数据库同步。 automatic dirty checking mechanism处理此问题。
PersistenceContext是会话级缓存。托管对象始终具有标识符和关联的数据库行。
我怀疑第一次select-all-query返回实体 isSigned = false并将其放入PC,然后用户执行一些操作 抓取实体byID的操作,设置isSigned = true和just 提取的实体冲突已经在PC中呈现。
在相同的持久化上下文范围内,这种情况永远不会发生。如果通过查询加载实体,则实体将在第1级缓存中获取缓存。如果您尝试使用另一个查询或使用EntityManager.find()再次加载它,您仍将获得相同的对象引用,该引用已经被缓存。
如果第一个查询针对持久化上下文发生,第二个查询/查找将在第二个持久化上下文上发布,则每个持久化上下文都必须缓存自己正在查询的实体版本。
第一个对象有isSigned = false,第二个有isSigned = true和PC confused并返回不同的托管对象。但是怎么样 可能吗?
这不可能发生。持久化上下文始终保持实体对象的完整性。