Question

Why am I receiving the exception below if the type of both entities is PersistentLogin? I thought that would mean they are in the same entity group, but I guess that is an incorrect assumption. Any ideas how to fix this?

This is the code:

// the class is marked with @Transactional
@Override
public final void removeUserTokens(final String username) {
    final Query query = entityManager.createQuery(
        "SELECT p FROM PersistentLogin p WHERE username = :username");
    query.setParameter("username", username);

    for (Object token : query.getResultList()) {
        entityManager.remove(token);
    }
}

This is the exception:

Caused by: javax.persistence.PersistenceException: Illegal argument
    at org.datanucleus.jpa.NucleusJPAHelper.getJPAExceptionForNucleusException(NucleusJPAHelper.java:260)
    at org.datanucleus.jpa.EntityTransactionImpl.commit(EntityTransactionImpl.java:122)
    at org.datanucleus.store.appengine.jpa.DatastoreEntityTransactionImpl.commit(DatastoreEntityTransactionImpl.java:50)
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:467)
    ... 42 more
Caused by: java.lang.IllegalArgumentException: can't operate on multiple entity groups in a single transaction. found both Element {
  type: "PersistentLogin"
  name: "1WfCYx8bmwUGkjzP2PpmFA=="
}
 and Element {
  type: "PersistentLogin"
  name: "SfI0P8RVBjTvu0WHMSuaVA=="
}
Was it helpful?

Solution

Same entity group means that the entities have a common parent entity.

Two top-level entities are never in the same entity group.

Any ideas how to fix this?

The easiest way is to relax the transaction requirements. In your case that would mean deleting the PersistentLogin entities one by one (best effort loop, delete as much as you can, retry on errors, no guarantee of atomicity).

If you wanted to put the PersistentLogin for the same user in the same entity group, you would need to make rather big changes to your data model, with uncertain impact on overall performance.

With non-relational databases, you have to code your application without relying on transactions on the data store. They just do not support them to the extent that we have become used to.

OTHER TIPS

First, read carefully the following docs, especially the section "Using Cross-Group Transactions" http://code.google.com/appengine/docs/java/datastore/transactions.html

Information on what a cross-group transaction is: http://code.google.com/appengine/docs/java/datastore/overview.html#Cross_Group_Transactions

Note: You can run a transaction over at most 5 different groups!

For your production appengine, you must enable "High Replication Datastore" in your appengine dashboard and you must enable "Cross-group (XG) transactions" in your source code, jdoconfig.xml, or persistence.xml

For your development appengine server, you must ensure that you simulate high-replication datastore. This is described here

Note: if you launch appengine dev server commandline, do not forget to add the high replication datastore option:

~/appengine-java-sdk-1.6.2.1/bin/dev_appserver.sh
    --jvm_flag=-Ddatastore.default_high_rep_job_policy_unapplied_job_pct=20
    --address=0.0.0.0 --port=8888 --disable_update_check .

We struggled some time with this while developing the Rogerthat Platform (code worked in production and in Eclipse, but not when dev_appserver.sh is executed commandline) so we thought it was worth sharing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top