【发布时间】:2013-04-05 10:54:52
【问题描述】:
在我的应用程序中,我保留了一个具有许多无主子级的实体。
似乎对 persist 的调用发生在事务中,因为当我不启用跨组事务时出现错误(并且子项生活在不同的实体组中)父实体)。
是否可以非事务性地进行持久化?
(如果需要此信息:我正在使用 Guice 将请求范围的 Provider<EntityManager> 注入到我的服务对象中。)
添加:
这是一个简单的测试用例:
@Entity public class Department {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
@Unowned
Collection<Employee> employees = new ArrayList<Employee>();
public Collection<Employee> getEmployees() {
return employees;
}
}
@Entity public class Employee {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne private Department department; private String name;
public Employee(String name) { this.name = name; } public String getName() { return name; }
public void setName(String name) { this.name = name; } }
现在,当我这样做时:
EntityManager em = EMF.get().createEntityManager();
try {
Department department = new Department();
department.getEmployees().add(new Employee("Joe"));
em.persist(department);
} finally {
em.close();
}
我明白了:
Illegal argument原因:
javax.persistence.PersistenceException:非法参数在 org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:298) 在 org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:197) 在 com.example.jpa.JpaServlet.doGet(JpaServlet.java:23) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:617) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 在 org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) 在 com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) 在 org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) 在 org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) 在 org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) 在 org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) 在 com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94) 在 org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 在 com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) 在 org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 在 org.mortbay.jetty.Server.handle(Server.java:326) 在 org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) 在 org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) 在 org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) 在 org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) 在 org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) 在 org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) 在 org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) 引起:java.lang.IllegalArgumentException:跨组事务 需要明确指定,请参阅 TransactionOptions.Builder.withXG 发现既 Element { type: “部门” id:17 } 和 Element { 类型:“员工” id:18 }
在 com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) 在 com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70) 在 com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:94) 在 com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) 在 com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) 在 com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) 在 com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) 在 com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) 在 com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) 在 com.google.appengine.datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) 在 com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) 在 org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) 在 org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) 在 org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1518) 在 com.google.appengine.datanucleus.scostore.AbstractFKStore.validateElementForWriting(AbstractFKStore.java:396) 在 com.google.appengine.datanucleus.scostore.FKListStore.validateElementForWriting(FKListStore.java:1036) 在 com.google.appengine.datanucleus.scostore.FKListStore.internalAdd(FKListStore.java:195) 在 com.google.appengine.datanucleus.scostore.FKListStore.addAll(FKListStore.java:114) 在 org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert(CollectionMapping.java:134) 在 com.google.appengine.datanucleus.StoreFieldManager.storeRelations(StoreFieldManager.java:809) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:367) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) 在 org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) 在 org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) 在 org.datanucleus.ObjectManagerImpl.flushInternalWithOrdering(ObjectManagerImpl.java:3888) 在 org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) 在 org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) 在 org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:4141) 在 org.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) 在 org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) 在 org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) 在 org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) 在 org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:193) ... 36 更多
原因:
java.lang.IllegalArgumentException: 需要跨组事务 明确指定,请参阅 TransactionOptions.Builder.withXGfound both 元素{类型:“部门”id:17}和元素{类型: “员工”编号:18 }
在 com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) 在 com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70) 在 com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:94) 在 com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) 在 com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) 在 com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) 在 com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) 在 com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) 在 com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) 在 com.google.appengine.datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) 在 com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) 在 org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) 在 org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) 在 org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1518) 在 com.google.appengine.datanucleus.scostore.AbstractFKStore.validateElementForWriting(AbstractFKStore.java:396) 在 com.google.appengine.datanucleus.scostore.FKListStore.validateElementForWriting(FKListStore.java:1036) 在 com.google.appengine.datanucleus.scostore.FKListStore.internalAdd(FKListStore.java:195) 在 com.google.appengine.datanucleus.scostore.FKListStore.addAll(FKListStore.java:114) 在 org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert(CollectionMapping.java:134) 在 com.google.appengine.datanucleus.StoreFieldManager.storeRelations(StoreFieldManager.java:809) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:367) 在 com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) 在 org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) 在 org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) 在 org.datanucleus.ObjectManagerImpl.flushInternalWithOrdering(ObjectManagerImpl.java:3888) 在 org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) 在 org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) 在 org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:4141) 在 org.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) 在 org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) 在 org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) 在 org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) 在 org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:193) 在 com.example.jpa.JpaServlet.doGet(JpaServlet.java:23) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:617) 在 javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 在 org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) 在 com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) 在 org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) 在 org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) 在 org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) 在 org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) 在 org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) 在 org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) 在 com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94) 在 org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 在 com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) 在 org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) 在 org.mortbay.jetty.Server.handle(Server.java:326) 在 org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) 在 org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) 在 org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) 在 org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) 在 org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) 在 org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) 在 org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
这就是为什么我一直认为persist 发生在事务中,尽管我还没有明确开始。
以下是来自 DataNucleus 的相关调试消息:
08:38:17,587 调试 [DataNucleus.Persistence] - ================= 持久化配置 ================ 08:38:17,587 DEBUG [DataNucleus.Persistence] - DataNucleus 持久性工厂 - 供应商: “DataNucleus”版本:“3.1.3”08:38:17,588 调试 [DataNucleus.Persistence] - DataNucleus 持久化工厂 为数据存储区 URL="appengine" driver="" userName="" 初始化 08:38:17,588 调试 [DataNucleus.Persistence] - JDK:Linux 上的 1.6.0_26 08:38:17,588 调试 [DataNucleus.Persistence] - 持久性 API:JPA 08:38:17,588 调试 [DataNucleus.Persistence] - 插件注册表: org.datanucleus.plugin.NonManagedPluginRegistry 08:38:17,588 调试 [DataNucleus.Persistence] - 持久性单元:事务-可选 08:38:17,588 调试 [DataNucleus.Persistence] - 标准选项: pm-singlethreaded、retain-values、nontransactional-read、 非事务性写入,serverTimeZone=UTC 08:38:17,589 调试 [DataNucleus.Persistence] - 持久性选项:关闭时分离 删除策略=JDO2 08:38:17,589 调试 [DataNucleus.Persistence] - 交易:类型=RESOURCE_LOCAL 模式=乐观 隔离=读取提交 08:38:17,589 调试 [DataNucleus.Persistence] - 值生成:txn-isolation=read-committed connection=New 08:38:17,589 DEBUG [DataNucleus.Persistence] - 类加载:jdo 08:38:17,589 调试 [DataNucleus.Persistence] - 缓存:Level1(软), Level2 (soft, mode=UNSPECIFIED), QueryResults (soft), Collections/Maps 08:38:17,589 调试 [DataNucleus.Persistence] - ==================================================== ========= 08:38:17,657 调试 [DataNucleus.Persistence] - 对象管理器 为数据存储打开“org.datanucleus.ObjectManagerImpl@e4eb585” “com.google.appengine.datanucleus.DatastoreManager@516f3619”与 txn="org.datanucleus.TransactionImpl@5c48cd13" 08:38:17,688 调试 [DataNucleus.Persistence] - 使对象持久化: “com.example.jpa.Department@55b7bf86”08:38:17,736 调试 [DataNucleus.Persistence] - 对象 “com.example.jpa.Department@55b7bf86”已被标记为持久性 但它对数据存储的实际持久性将由于使用而延迟 乐观事务或“delayDatastoreOperationsUntilCommit” 08:38:17,741 调试 [DataNucleus.Persistence] - 制作对象 持久性:“com.example.jpa.Employee@95d0a50”08:38:17,742 调试 [DataNucleus.Persistence] - 对象“com.example.jpa.Employee@95d0a50” 已被标记为持久性,但它的实际持久性 数据存储将由于使用乐观事务而延迟或 “delayDatastoreOperationsUntilCommit” 08:38:17,747 调试 [DataNucleus.Transaction] - 事务创建 [DataNucleus 交易,ID=Xid= 08:38:17,748 调试 [DataNucleus.Transaction] - 事务开始于 ObjectManager org.datanucleus.ObjectManagerImpl@e4eb585 (乐观=真)08:38:17,757 调试 [DataNucleus.Transaction] - 对资源运行登记操作: com.google.appengine.datanucleus.DatastoreXAResource@7d7082d8,错误 代码 TMNOFLAGS 和事务:[DataNucleus 事务,ID=Xid= 08:38:18,024 调试 [DataNucleus.Transaction] - 启动数据存储 交易:0 08:38:18,026 调试 [DataNucleus.Transaction] - ObjectManager 的事务提交 org.datanucleus.ObjectManagerImpl@e4eb585 08:38:18,027 调试 [DataNucleus.Persistence] - ObjectManager.internalFlush() 过程 开始使用有序刷新 - 2 个脏对象 08:38:18,105 DEBUG [DataNucleus.Persistence] - 管理类的持久性: com.example.jpa.Employee [表:com.example.jpa.Employee, InheritanceStrategy : new-table] 08:38:18,136 调试 [DataNucleus.Persistence] - 管理类的持久性: com.example.jpa.Department [表:com.example.jpa.Department, InheritanceStrategy : new-table] 08:38:18,316 调试 [DataNucleus.Persistence] - 字段 “com.example.jpa.Department.employees” 被持续存在 “级联持续”。 08:38:18,331 信息 [DataNucleus.Persistence] - 对象“com.example.jpa.Department@55b7bf86”有一个集合 “com.example.jpa.Department.employees” 元素 “com.example.jpa.Employee@95d0a50”没有设置所有者。管理 关系和设置所有者。 08:38:18,333 警告 [DataNucleus.MetaData] - 元数据警告 com.example.jpa.Employee.department:元数据错误 com.example.jpa.Employee.department :数据存储不支持 加入,因此不能接受将相关对象放入的请求 默认提取组。该字段将首先延迟获取 使用权。您可以通过设置 datanucleus.appengine.ignorableMetaDataBehavior 属性 配置。 NONE 值将使警告静音。错误值 会将警告变成异常。 08:38:18,354 调试 [DataNucleus.Persistence] - ObjectManager.internalFlush() 过程 完成 08:38:18,355 调试 [DataNucleus.Transaction] - 非法 参数 org.datanucleus.exceptions.NucleusFatalUserException:非法 论据
【问题讨论】:
-
为什么不展示你的代码来说明你是如何坚持下去的?并在 DEBUG 级别显示日志以获取此类持久性
-
在我设置了一个测试用例来重现我的发现之后,我会这样做。
-
希望添加的代码和堆栈跟踪有所帮助。
标签: google-app-engine jpa guice datanucleus guice-persist