Domanda

In my application I am persisting an entity that has a number of unowned children.

It seems that the call to persist which does the persisting of the entity and all children happens inside a transaction because I get an error when I do not enable cross-group transactions (and the children live in different entity groups as the parent entity).

Is it possible to have the persisting being done non-transactional?

(In case this information is needed: I am using Guice to inject a request-scoped Provider<EntityManager> into my service object.)

ADDED:

Here is a simple test case:

@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;   }    }

Now, when I do:

EntityManager em = EMF.get().createEntityManager();
try {
    Department department = new Department();
    department.getEmployees().add(new Employee("Joe"));
    em.persist(department);
} finally {
    em.close();
}

I get:

Illegal argument

Caused by:

javax.persistence.PersistenceException: Illegal argument at org.datanucleus.api.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:298) at org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:197) at com.example.jpa.JpaServlet.doGet(JpaServlet.java:23) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle(Server.java:326) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582) Caused by: java.lang.IllegalArgumentException: cross-group transaction need to be explicitly specified, see TransactionOptions.Builder.withXGfound both Element { type: "Department" id: 17 } and Element { type: "Employee" id: 18 }

at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) at com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70) at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:94) at com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) at com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) at com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) at com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) at com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) at com.google.appengine.datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) at com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) at org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) at org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) at org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1518) at com.google.appengine.datanucleus.scostore.AbstractFKStore.validateElementForWriting(AbstractFKStore.java:396) at com.google.appengine.datanucleus.scostore.FKListStore.validateElementForWriting(FKListStore.java:1036) at com.google.appengine.datanucleus.scostore.FKListStore.internalAdd(FKListStore.java:195) at com.google.appengine.datanucleus.scostore.FKListStore.addAll(FKListStore.java:114) at org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert(CollectionMapping.java:134) at com.google.appengine.datanucleus.StoreFieldManager.storeRelations(StoreFieldManager.java:809) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:367) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) at org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) at org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) at org.datanucleus.ObjectManagerImpl.flushInternalWithOrdering(ObjectManagerImpl.java:3888) at org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) at org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) at org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:4141) at org.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) at org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) at org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) at org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:193) ... 36 more

Caused by:

java.lang.IllegalArgumentException: cross-group transaction need to be explicitly specified, see TransactionOptions.Builder.withXGfound both Element { type: "Department" id: 17 } and Element { type: "Employee" id: 18 }

at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) at com.google.appengine.api.datastore.DatastoreApiHelper$1.convertException(DatastoreApiHelper.java:70) at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:94) at com.google.appengine.api.datastore.FutureHelper$CumulativeAggregateFuture.get(FutureHelper.java:142) at com.google.appengine.api.datastore.FutureHelper$TxnAwareFuture.get(FutureHelper.java:218) at com.google.appengine.api.utils.FutureWrapper.get(FutureWrapper.java:86) at com.google.appengine.api.datastore.FutureHelper.getInternal(FutureHelper.java:71) at com.google.appengine.api.datastore.FutureHelper.quietGet(FutureHelper.java:32) at com.google.appengine.api.datastore.DatastoreServiceImpl.put(DatastoreServiceImpl.java:85) at com.google.appengine.datanucleus.WrappedDatastoreService.put(WrappedDatastoreService.java:112) at com.google.appengine.datanucleus.EntityUtils.putEntitiesIntoDatastore(EntityUtils.java:766) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:314) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) at org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) at org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) at org.datanucleus.store.types.sco.SCOUtils.validateObjectForWriting(SCOUtils.java:1518) at com.google.appengine.datanucleus.scostore.AbstractFKStore.validateElementForWriting(AbstractFKStore.java:396) at com.google.appengine.datanucleus.scostore.FKListStore.validateElementForWriting(FKListStore.java:1036) at com.google.appengine.datanucleus.scostore.FKListStore.internalAdd(FKListStore.java:195) at com.google.appengine.datanucleus.scostore.FKListStore.addAll(FKListStore.java:114) at org.datanucleus.store.mapped.mapping.CollectionMapping.postInsert(CollectionMapping.java:134) at com.google.appengine.datanucleus.StoreFieldManager.storeRelations(StoreFieldManager.java:809) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObjectsInternal(DatastorePersistenceHandler.java:367) at com.google.appengine.datanucleus.DatastorePersistenceHandler.insertObject(DatastorePersistenceHandler.java:218) at org.datanucleus.state.JDOStateManager.internalMakePersistent(JDOStateManager.java:2381) at org.datanucleus.state.JDOStateManager.flush(JDOStateManager.java:3778) at org.datanucleus.ObjectManagerImpl.flushInternalWithOrdering(ObjectManagerImpl.java:3888) at org.datanucleus.ObjectManagerImpl.flushInternal(ObjectManagerImpl.java:3811) at org.datanucleus.ObjectManagerImpl.flush(ObjectManagerImpl.java:3751) at org.datanucleus.ObjectManagerImpl.preCommit(ObjectManagerImpl.java:4141) at org.datanucleus.ObjectManagerImpl.transactionPreCommit(ObjectManagerImpl.java:428) at org.datanucleus.TransactionImpl.internalPreCommit(TransactionImpl.java:398) at org.datanucleus.TransactionImpl.commit(TransactionImpl.java:287) at org.datanucleus.ObjectManagerImpl.close(ObjectManagerImpl.java:1090) at org.datanucleus.api.jpa.JPAEntityManager.close(JPAEntityManager.java:193) at com.example.jpa.JpaServlet.doGet(JpaServlet.java:23) at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1166) at com.google.appengine.api.socket.dev.DevSocketFilter.doFilter(DevSocketFilter.java:74) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.ResponseRewriterFilter.doFilter(ResponseRewriterFilter.java:123) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.HeaderVerificationFilter.doFilter(HeaderVerificationFilter.java:34) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.api.blobstore.dev.ServeBlobFilter.doFilter(ServeBlobFilter.java:61) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.apphosting.utils.servlet.TransactionCleanupFilter.doFilter(TransactionCleanupFilter.java:43) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.StaticFileFilter.doFilter(StaticFileFilter.java:125) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at com.google.appengine.tools.development.BackendServersFilter.doFilter(BackendServersFilter.java:97) at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157) at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388) at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216) at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182) at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765) at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418) at com.google.appengine.tools.development.DevAppEngineWebAppContext.handle(DevAppEngineWebAppContext.java:94) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at com.google.appengine.tools.development.JettyContainerService$ApiProxyHandler.handle(JettyContainerService.java:409) at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152) at org.mortbay.jetty.Server.handle(Server.java:326) at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542) at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:923) at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:547) at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212) at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404) at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409) at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

That's why I have been thinking that persist happens inside a transaction although I haven't begun one explicitly.

Here are the relevant debug messages from DataNucleus:

08:38:17,587 DEBUG [DataNucleus.Persistence] - ================= Persistence Configuration =============== 08:38:17,587 DEBUG [DataNucleus.Persistence] - DataNucleus Persistence Factory - Vendor: "DataNucleus" Version: "3.1.3" 08:38:17,588 DEBUG [DataNucleus.Persistence] - DataNucleus Persistence Factory initialised for datastore URL="appengine" driver="" userName="" 08:38:17,588 DEBUG [DataNucleus.Persistence] - JDK : 1.6.0_26 on Linux 08:38:17,588 DEBUG [DataNucleus.Persistence] - Persistence API : JPA 08:38:17,588 DEBUG [DataNucleus.Persistence] - Plugin Registry : org.datanucleus.plugin.NonManagedPluginRegistry 08:38:17,588 DEBUG [DataNucleus.Persistence] - Persistence-Unit : transactions-optional 08:38:17,588 DEBUG [DataNucleus.Persistence] - Standard Options : pm-singlethreaded, retain-values, nontransactional-read, nontransactional-write, serverTimeZone=UTC 08:38:17,589 DEBUG [DataNucleus.Persistence] - Persistence Options : detach-on-close deletion-policy=JDO2 08:38:17,589 DEBUG [DataNucleus.Persistence] - Transactions : type=RESOURCE_LOCAL mode=optimistic isolation=read-committed 08:38:17,589 DEBUG [DataNucleus.Persistence] - Value Generation : txn-isolation=read-committed connection=New 08:38:17,589 DEBUG [DataNucleus.Persistence] - ClassLoading : jdo 08:38:17,589 DEBUG [DataNucleus.Persistence] - Cache : Level1 (soft), Level2 (soft, mode=UNSPECIFIED), QueryResults (soft), Collections/Maps 08:38:17,589 DEBUG [DataNucleus.Persistence] - =========================================================== 08:38:17,657 DEBUG [DataNucleus.Persistence] - Object Manager "org.datanucleus.ObjectManagerImpl@e4eb585" opened for datastore "com.google.appengine.datanucleus.DatastoreManager@516f3619" with txn="org.datanucleus.TransactionImpl@5c48cd13" 08:38:17,688 DEBUG [DataNucleus.Persistence] - Making object persistent : "com.example.jpa.Department@55b7bf86" 08:38:17,736 DEBUG [DataNucleus.Persistence] - Object "com.example.jpa.Department@55b7bf86" has been marked for persistence but its actual persistence to the datastore will be delayed due to use of optimistic transactions or "delayDatastoreOperationsUntilCommit" 08:38:17,741 DEBUG [DataNucleus.Persistence] - Making object persistent : "com.example.jpa.Employee@95d0a50" 08:38:17,742 DEBUG [DataNucleus.Persistence] - Object "com.example.jpa.Employee@95d0a50" has been marked for persistence but its actual persistence to the datastore will be delayed due to use of optimistic transactions or "delayDatastoreOperationsUntilCommit" 08:38:17,747 DEBUG [DataNucleus.Transaction] - Transaction created [DataNucleus Transaction, ID=Xid= 08:38:17,748 DEBUG [DataNucleus.Transaction] - Transaction begun for ObjectManager org.datanucleus.ObjectManagerImpl@e4eb585 (optimistic=true) 08:38:17,757 DEBUG [DataNucleus.Transaction] - Running enlist operation on resource: com.google.appengine.datanucleus.DatastoreXAResource@7d7082d8, error code TMNOFLAGS and transaction: [DataNucleus Transaction, ID=Xid= 08:38:18,024 DEBUG [DataNucleus.Transaction] - Started datastore transaction: 0 08:38:18,026 DEBUG [DataNucleus.Transaction] - Transaction committing for ObjectManager org.datanucleus.ObjectManagerImpl@e4eb585 08:38:18,027 DEBUG [DataNucleus.Persistence] - ObjectManager.internalFlush() process started using ordered flush - 2 dirty objects 08:38:18,105 DEBUG [DataNucleus.Persistence] - Managing Persistence of Class : com.example.jpa.Employee [Table : com.example.jpa.Employee, InheritanceStrategy : new-table] 08:38:18,136 DEBUG [DataNucleus.Persistence] - Managing Persistence of Class : com.example.jpa.Department [Table : com.example.jpa.Department, InheritanceStrategy : new-table] 08:38:18,316 DEBUG [DataNucleus.Persistence] - Field "com.example.jpa.Department.employees" is being persisted for "cascade-persist". 08:38:18,331 INFO [DataNucleus.Persistence] - Object "com.example.jpa.Department@55b7bf86" has a collection "com.example.jpa.Department.employees" yet element "com.example.jpa.Employee@95d0a50" doesnt have the owner set. Managing the relation and setting the owner. 08:38:18,333 WARN [DataNucleus.MetaData] - Meta-data warning for com.example.jpa.Employee.department: Error in meta-data for com.example.jpa.Employee.department : The datastore does not support joins and therefore cannot honor requests to place related objects in the default fetch group. The field will be fetched lazily on first access. You can modify this warning by setting the datanucleus.appengine.ignorableMetaDataBehavior property in your config. A value of NONE will silence the warning. A value of ERROR will turn the warning into an exception. 08:38:18,354 DEBUG [DataNucleus.Persistence] - ObjectManager.internalFlush() process finished 08:38:18,355 DEBUG [DataNucleus.Transaction] - Illegal argument org.datanucleus.exceptions.NucleusFatalUserException: Illegal argument

È stato utile?

Soluzione

Just faced with the same issue and can confidently say that transaction committed when you call

EntityManager.close();

Here is some prove from DataNucleus JPA implementation

Altri suggerimenti

a bit late but I thought it would be useful for others. I have faced the same issue while using JPA with Google App Engine. But following code worked for me. Hope it helps-

try{
    EntityTransaction t = em.getTransaction();
    try{
        t.begin();
        //Do stuff
        em.persist(persistObject);
        t.commit();
    }finally {
        if (t.isActive())
            t.rollback();
    }
} finally {
    em.close();
}

You need to specify the that your transaction is cross group transaction for jpa. read below document specially cross group transaction for jpa/jdo

https://cloud.google.com/appengine/docs/java/datastore/transactions?csw=1

for my case adding below line in the persistance.xml resolved this issue.

<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true"/>

Clearly from the exception

java.lang.IllegalArgumentException: cross-group transaction need to be explicitly specified, see TransactionOptions.Builder.withXGfound both Element { type: "Department" id: 17 } and Element { type: "Employee" id: 18 }

at com.google.appengine.api.datastore.DatastoreApiHelper.translateError(DatastoreApiHelper.java:33) 

the datastore is running in a (low-level) transaction (shown in the log too), and it requires multi-entity-groups to be enabled to do what you want there.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top