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=="
}
Same entity group means that the entities have a common parent entity.
Two top-level entities are never in the same entity group.
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.
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:
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.