MikO
MikO

Reputation: 18751

Retrieving an object from Datastore by an attribute in its child object

I'm creating a Java web app in Google App Engine with JDO.

I have a class User that has an attribute SessionToken. Both classes are persistent with an owned one-to-one relationship.

@PersistenceCapable
public class User {    
  @PrimaryKey
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;
  @Persistent
  private SessionToken sessionToken;    
  //getters and setters...
}

@PersistenceCapable
public class SessionToken { 
  @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
  private Key key;
  @Persistent
  private String token;    
  //getters and setters...
}

Now I'd like to have a method to get an user from the Datastore using the attribute token of the SessionToken. I mean, I'd need something like this:

Query query = pm.newQuery(User.class);
query.setFilter("sessionToken.token == tokenParam");
query.declareParameters("String tokenParam");
//return retrieved user if any...

But this isn't working because the query can't access to the attributes in the child object.

It throws this exception:

javax.jdo.JDOFatalUserException: SELECT FROM com.mockgaeapp.User 
WHERE sessionToken.token == tokenParam PARAMETERS String tokenParam: 
Can only reference properties of a sub-object if the sub-object is embedded.

The only way I can do it is getting first the SessionToken and then the correspondent User, something like:

//First retrieve the SessionToken...
Query query = pm.newQuery(SessionToken.class);
query.setFilter("token == tokenParam");
query.declareParameters("String tokenParam");
List<SessionToken> tokens = (List<SessionToken>) query.execute(sessionToken);
if (!tokens.isEmpty()) {

  //... then retrieve the User
  query = pm.newQuery(User.class);
  query.setFilter("sessionToken == sessionTokenParam");
  query.declareParameters("String sessionTokenParam");
  List<User> users = (List<User>) query.execute(tokens.get(0));
  //return retireved user if any...
}

I was wondering if there's other way to do it, using only one query, or even if there's some way else... Moreover we have to take into account that this operation will be performed many times...

EDIT: I've found a simpler way that is also working. Once I have retrieved the SessionToken as in the last example, I use its key to retrieve the User like:

//... then retrieve the User
user = pm.getObjectById(User.class, tokens.get(0).getKey().getParent());
//return retireved user if any...

This way I do a key lookup instead of another query, but anyway I still access the Datastore twice...

Upvotes: 1

Views: 414

Answers (2)

michal-husak
michal-husak

Reputation: 153

Query query = pm.newQuery(User.class);
query.setFilter("sessionToken == tokenParam && tokenParam == 'my_token'");
query.declareParameters(SessionToken.class.getName() + " tokenParam");

should work

Upvotes: 0

Ian Marshall
Ian Marshall

Reputation: 760

One possible solution is to have a bidirectional owned one-to-one relationship between your two entity classes. Then you can:

  1. Obtain the SessionToken you want by querying on token.
  2. Obtain the SessionToken's parent User by calling the appropriate getter method of the obtained SessionToken.

Another possible solution is to follow "Executing Simple Joins Across Owned Relationships" as set out in the "Google App Engine Java Persistence" blog.

Upvotes: 1

Related Questions