I have a JDO Card entity with an externally generated Key (read from a physical card). This links to a User entity, which is created at the same time as the Card entity. When I read the card and call a method, I want AppEngine to either return me the Card and User previously created, or to create a new one. However, if I call the method twice in rapid succession, I end up with the Card entity being overwritten and two User identities being created.
Here’s my code (I’ve removed getters & setters):
CardEntity.java
@PersistenceCapable public class CardEntity {
@PrimaryKey
private Key Id;
@Persistent
@Unowned
private UserEntity user;
}
UserEntity.java
@PersistenceCapable
public class CustomerEntity {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key key;
@Persistent
private String name;
}
method from Controller.java
public UserEntity getOrCreateUser(String id) {
CardEntity cardEntity;
UserEntity userEntity;
Query query;
PersistenceManager mgr = getPersistenceManager();
Transaction tx = mgr.currentTransaction();
boolean complete = false;
while (!complete) {
try {
tx.begin();
query = mgr.newQuery(CardEntity.class);
query.setUnique(true);
// rootkey is a key for a CardEntity that I am using as the parent for
// all CardEntities, so they are in the same enitity group, as a hack
// to try to solve this problem.
Key key = KeyFactory.createKey(rootKey, "CardEntity", id);
query.setFilter("id == :id");
cardEntity = (CardEntity) query.execute(key);
if (CardEntity == null) {
cardEntity = new CardEntity();
cardEntity.setId(key);
userEntity = new UserEntity();
mgr.makePersistent(userEntity);
nfcCardEntity.setUser(UserEntity);
mgr.makePersistent(cardEntity);
CardEntity rootCardEntity =
(CardEntity) mgr.getObjectById(CardEntity.class, rootKey);
rootCardEntity.setUser(userEntity);
// this is another hack to ensure that something changes in this transaction
// so that if there's a conflict, it will be rolled back
mgr.makePersistent(rootCardEntity);
} else {
userEntity = cardEntity.getUser();
}
tx.commit();
} finally {
if (tx.isActive()) {
tx.rollback();
} else {
complete = true;
}
}
}
mgr.close();
return userEntity;
}
I’ve tried numerous things, but I frequently end up with multiple UserEntities being created. The above code has all the CardEntities in one Entity Group (defined by rootkey), and I’ve even included a modification on the root Entity as well. This doesn’t seem to help.
Any tips, pointers?
go for:
getObjectByIdand catch the exception.getObjectById have strong consitency wich means, it should immediately return an Object if it is there right after creation.
Where the Query only have eventual consistency, which means it maybe found after creation but also maybe not.
in case not found
getObjectByIdthrows a Entity not found exception which can catch and handle separately.