»
March 12, 2010
»

Gaeds 1.2 Changes #4 - Inheritance

Gaeds is my tiny AppEngine for Java low-level DatastoreService wrapper. This is forth post about new features that will form 1.2 final release.

The first three are:

Both new features are related to inheritance handling so lets start with 3 models:

@Model
public class Transaction {
}

@Model(discriminator = "handled")
public class HandledTransaction extends Transaction {
}

@Model(discriminator = "amount")
public class AmountTransaction extends HandledTransaction {
}

Those three models shares single kind of "Transaction".

Query keysOnly() implementation change

Now it is possible to query for keysOnly() while getting (more) correct model classes:

Iterable<HandledTransaction> iterable = getSession().query().
  model(HandledTransaction.class).
  eq("failed", false).
  eq("otherTransactionKey", null).
  limit(limit).
  keysOnly().
  asIterable();

for (HandledTransaction transaction : iterable) {
  // ...
}

Prepared query is:

SELECT __key__ 
  FROM Transaction 
  WHERE _discriminator = handled 
    AND failed = false 
    AND otherTransactionKey = NULL

While keysOnly() query doesn’t return actual type (In this case it can also be AmountTransaction — see next feature why) at least it is not base Transaction instance anymore.

Here is all three queries:

session.query().model(<Model>.class).keysOnly().asList();
Model and returned instance class DatastoreService Query
Transaction.class SELECT __key__ FROM Transaction
HandledTransaction.class SELECT __key__ FROM Transaction WHERE _discriminator = handled
AmountTransaction.class SELECT __key__ FROM Transaction WHERE _discriminator = amount

Of course this also works for SELECT * FROM queries with the difference that returned instance clases will be actual sub-classes:

session.query().model(<Model>.class).asList();
Model and returned instance class DatastoreService Query
Transaction.class SELECT * FROM Transaction
HandledTransaction.class SELECT * FROM Transaction WHERE _discriminator = handled
AmountTransaction.class SELECT * FROM Transaction WHERE _discriminator = amount

Discriminator as list property (when needed)

As we saw previously to query any HandledTransaction (this includes all HandledTransaction and also AmountTransaction entities) we need to query with two different "_discriminator" values. This is not supported in AppEngine Datastore. Instead if given model is subclass of model which is already a model what has discriminator value set, the "_discriminator" property becomes list property listing all discriminators:

Model Discriminators
Transaction.class
HandledTransaction.class “handled” — String
AmountTransaction.class [“amount”, “handled”] — List<String>

This allows to query any entity that is HandledTransaction or AmountTransaction.

Upgrading Entities (if needed, optional)

To take advantage of those new features, enties what has parent entity with discriminator set (in this example only AmountTransaction), needs to be migrated.

To migrate just get or query them followed by put afterwards:

Session session = getSession();

// Query will work because this was the _discriminator value that was saved before
// SELECT * FROM Transaction WHERE _discriminator = amount
Iterable<AmountTransaction> list = session.query().
  model(AmountTransaction.class).
  limit(500).
  asIterable();

// gaeds will re-populate _discriminator value
session.put(list);
 
Internet Explorer 6
Are you serious?