JDO中何时调用Query.close与关闭Persistence Manager?结果关闭错误求解
问题解决方案
问题原因
JDO的查询结果集是延迟加载的,返回的List只是一个代理对象,实际数据并未立刻从Datastore加载到内存;同时查询返回的User对象是附着在PersistenceManager(PM)上的持久化实例,一旦PM关闭,这些实例就无法访问。你尝试用new ArrayList<>(user)复制时,部分JDO实现可能不会立刻遍历整个集合触发加载,或者复制后的List依然依赖PM的连接,导致后续访问时PM已关闭,抛出“Query result has been closed”错误。
解决方法
核心思路是在关闭PM和Query前,将数据完全加载为脱离PM依赖的普通对象,以下两种方式均可:
1. 使用JDO对象分离API
通过pm.detachCopyAll()将查询结果转换为脱离PM的普通对象,返回的List完全独立,不受后续PM关闭的影响:
public List<User> findAll() { LOGGER.info("Getting users list"); Query query = null; PersistenceManager pm = PMF.get().getPersistenceManager(); try { query = pm.newQuery(User.class); List<User> result = (List<User>) query.execute(); // 分离所有对象,解除对PM的依赖 List<User> detachedUsers = pm.detachCopyAll(result); return new ArrayList<>(detachedUsers); } catch (Exception e) { LOGGER.error("Error while getting user list", e); return Collections.emptyList(); } finally { if(query != null) query.closeAll(); pm.close(); } }
2. 手动遍历触发完全加载
在PM关闭前手动遍历整个结果集,将每个对象复制到新List中,确保所有数据都加载到内存:
public List<User> findAll() { LOGGER.info("Getting users list"); Query query = null; PersistenceManager pm = PMF.get().getPersistenceManager(); try { query = pm.newQuery(User.class); List<User> userList = new ArrayList<>(); // 遍历结果集,触发所有数据加载到内存 for (User user : (List<User>) query.execute()) { userList.add(user); } return userList; } catch (Exception e) { LOGGER.error("Error while getting user list", e); return Collections.emptyList(); } finally { if(query != null) query.closeAll(); pm.close(); } }
关于关闭资源的说明
是的,必须关闭Query和Persistence Manager:
- PersistenceManager对应Datastore的连接资源,不关闭会导致连接泄漏,耗尽数据库连接池;
- Query的
closeAll()会释放查询相关的游标、临时缓存等资源,避免资源浪费。
关键要确保在关闭资源之前,已完成数据的加载和分离,让返回的List不再依赖这些资源。
内容的提问来源于stack exchange,提问作者Kiran Subedi
相关产品推荐
相关产品推荐

