Key fields are not retrieved by default
Posted by David Chandler on November 6, 2009
I’ve noticed that AppEngine does not populate Key fields by default when querying. I’m sure it’s documented, but I can’t remember where. At any rate, you can annotate with defaultFetchGroup to resolve this. Same goes for properties of type Text and Blob.
... import com.google.appengine.api.datastore.Key; @PersistenceCapable(identityType = IdentityType.APPLICATION) public class PrayerItem implements Serializable { @PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY) private Long id; @Persistent(defaultFetchGroup="true") private Key list; ... }
In the case of owned relationships, where a property is represented by its actual domain class rather than a Key, it is also necessary to use defaultFetchGroup=”true”. Even though owned properties are in the same entity group and are therefore eligible for inclusion in a transaction, the AppEngine Datastore doesn’t allow joins, so owned relationships are not returned in the initial query. When you set defaultFetchGroup=”true” on an owned property, you will get this warning:
WARNING: Meta-data warning for com…your_owned_property_name: The datastore does not support joins and therefore cannot honor requests to place child objects in the default fetch group. The field will be fetched lazily on first access. You can modify this warning by setting the datanucleus.appengine.ignorableMetaDataBehavior property in your config. A value of NONE will silence the warning. A value of ERROR will turn the warning into an exception.
Lazy fetching does work as described in the message.
Correction: AppEngine Datastore and owned relationships « TurboManage said
[…] relationship. This was not correct, as AppEngine Datastore never allows joins. I’ve updated the original post […]
Rusty Wright said
What do you do if the persistence manager is closed? Doesn’t the lazy fetch only work if the persistence manager is still open? I’m using spring and my service layer has spring’s @Transactional on its classes and spring closes the persistence manager when the methods return.
David Chandler said
Good clarification, thanks. Lazy fetching only works as long as the PM is open. Therefore, if you need to return a fully populated object graph to the client, you must call the getters for the joined fields before closing the PM. Typically, this means within the same service method that initiated the query.
Chris said
I have found that the defaultFetchGroup=”true” does work as described on app engine. I have also found in the documentation where it lays out an exact example of using this syntax.
http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html
Child objects are loaded from the datastore when they are accessed for the first time. If you do not access the child object on a parent object, the entity for the child object is never loaded. If you want to load the child, you can either “touch” it before closing the PersistenceManager (e.g. by calling getContactInfo() in the above example) or explicitly add the child field to the default fetch group so it’s retrieved and loaded with the parent:
Employee.java
import ContactInfo;
// …
@Persistent(defaultFetchGroup = “true”)
private ContactInfo contactInfo;
The downside is I still get the Warning: Meta-data message when GAE executes the query. Is the documentation wrong or should the warning be written up as a bug?
David Chandler said
Thanks, Chris. App Engine has come a long way in the 17 months since I wrote this, and shortly after, I switched from JDO to Objectify, so I’m not in the loop on JDO issues now.
branflake2267 said
How do you like objectify compared to JDO?
David Chandler said
I much prefer Objectify.
branflake2267 said
For those who are having this problem even though you have deeper children you’ll need to modify the jdoconfig.xml file and change the value to 2. If you have parent child relationship like myjdoclass(1)/myjdoclass(2)/myjdoclass(3)… you need to set the depth. I’m assuming “touch” means to write?
/* file: jdoconfig.xml */
This is noted in documentation: http://code.google.com/appengine/docs/java/datastore/jdo/relationships.html#Owned_One-to-One_Relationships – look for “touch”
The documentation isn’t entirely clear what is going on with owned child collection relationships. Thanks for documenting this David because saved me a ton of time.
Brandon Donnelson
http://gwt-examples.googlecode.com
branflake2267 said
Oops the property disappeared under the file above, thinking it was html.
/* file jdoconfi.xml */
property name=”datanucleus.maxFetchDepth” value=”2″ – (add the greaterthan and less then…)