- summary Defining and registering entities
<wiki:toc max_depth="3"></wiki:toc>
Entities are simple Java POJOs with a handful of special annotations. Objectify has its own annotations and *does NOT use JPA or JDO annotations*.
Note that throughout this documentation we will leave off getter and setter methods for brevity.
Here is a minimal entity class:
Observe:
* Entity classes must be annotated with {{{@Entity}}}.
* Objectify persists fields and only fields. It does not arbitrarily map fields to the datastore; if you want to change the name of a property in the datastore, rename the field. Getters and setters are ignored so you can isolate the public interface of your class (eg, {{{public String getVehicleIdentificationNumber() { return vin; }}}}).
* Objectify will not persist {{{static}}} fields, {{{final}}} fields, or fields annotated with {{{@Ignore}}}. It *will* persist fields with the {{{transient}}} keyword, which only affects serialization.
* Entities must have have one field annotated with {{{@Id}}}. The actual name of the field is irrelevant and can be renamed at any time, even after data is persisted. This value (along with the kind 'Car') becomes part of the {{{Key}}} which identifies an entity.
* The {{{@Id}}} field can be of type {{{Long}}}, {{{long}}}, or {{{String}}}. If you use {{{Long}}} and save an entity with a null id, a numeric value will be generated for you using the standard GAE allocator for this kind. If you use {{{String}}} or the primitive {{{long}}} type, values will never be autogenerated.
* There must be a no-arg constructor (or no constructors - Java creates a default no-arg constructor). The no-arg constructor can have any protection level (private, public, etc).
* {{{String}}} fields which store more than 500 characters (the GAE limit) are automatically converted to {{{Text}}} internally. Note that {{{Text}}} fields, like {{{Blob}}} fields, are never indexed (see [Queries]).
* {{{byte[]}}} fields are automatically converted to {{{Blob}}} internally. However, {{{Byte[]}}} is persisted "normally" as an array of (potentially indexed) {{{Byte}}} objects. Note that GAE natively stores all integer values as a 64-bit long.
Objectify allows you to nest embedded classes:
Any class which is not recognized as a core value type or a special Objectify type (like ) will be broken down into its component fields and stored natively as an EmbeddedEntity. There are no limitations on how deep you can nest embedded structures. Embedded classes are treated just like entities - they can be polymorphic and have lifecycle methods.
There is a special case when you embed a class which is itself an :
This will *not* create a foreign key reference! This will _embed_ the entity in the entity. The key of the will be used as the native stored in the . To create relationships between entities, see the later section on [#Relationships]
If you place on a field in an embedded class, Objectify will populate it with a reference to the appropriate containing class:
In deeply nested structures, fields will be populated with the first structure found going "up" that matches the correct type.
fields are not persisted in the native datastore. They are populated only when the entity is loaded.
All embedded objects (both @Entity classes and vanilla classes) are stored as .
GAE does not natively support indexing of fields. To allow you to query on embedded data, Objectify creates synthetic index properties in the top-level Entity objects. These are dot-separated property names used only for queries; they are not used to load your POJO objects.
Unfortunately, the GAE Datastore Viewer currently shows objects and collections of objects as opaque blobs. Please star this issue.
Collections and arrays are embedded normally, and can contain nested classes. You are encouraged to initialize collections in your constructor:
If you initialize collections like this, Objectify will clear and recycle the collection when loading entities. This will preserve comparators or any other state relevant to the collection. If you do not initialize collections, Objectify will take a best guess at the appropriate type based on the field (eg will be initialized to ).
Arrays cannot be initialized in this manner.
is treated specially - it is automatically converted to and from native Blob representation in the datastore. Other arrays of number types (including Byte[]) are stored natively as collections.
Objectify will store a as an in the datastore. As with property names, the key must be a String:
The value can be any type that can be persisted as a field, including basic types, embedded objects, and collections. Note that because this is stored as `EmbeddedEntity`, which is based on a `HashMap`, ordering of map values is unpredictable - even if your field is of type `LinkedHashMap`.
In order to use non-String keys with Maps, you may specify the annotation:
Because it is commonly useful, Objectify provides which automatically converts between and its web-safe string representation.
Perhaps you have a native collection of embedded objects which you would prefer to access as a Map:
The field will be stored natively as a . When Objectify loads this field, it will use the to obtain a key for each object and store the entry in the map.
The value of this map can be any type that can be persisted by Objectify.
Objectify lets you define a polymorphic hierarchy of related entity classes, and then load and query them without knowing the specific subtype. Here are some examples:
Things to note:
* All polymorphic subclasses must be annotated with {{{@Subclass}}} and registered explicitly.
* You can skip {{{@Subclass}}} on intermediate classes which will never be materialized or queried for.
* If you save an object of a type which is not a registered {{{@Subclass}}}, you will get an exception!
In a polymorphic hierarchy, you can without knowing the actual type:
Polymorphism applies to embedded objects exactly as it does to top level objects.
* You do not need {{{@Entity}}} at the root of your polymorphic hierarchy (unless you actually want to embed an entity with a real key). Objectify discovers the root of your polymorphic hierarchy when the field is declared.
* You must register all {{{@Subclass}}} classes which will be used.
* The {{{index}}} element of {{{@Subclass}}} is ignored.
Subclasses of polymorphic objects are stored with two additional, hidden synthetic properties:
* _^d_ holds a discriminator value for the concrete class type. This defaults to the class shortname but can be modified with the {{{@Subclass(name="Alternate")}}} annotation.
* _^i_ holds an indexed list of all the discriminators relavant to a class; for example a Cat would have [["Mammal", "Cat]]. Note that *subclasses are not indexed by default*. See below.
The indexed property is what allows polymorphic queries to work. It also means that you cannot simply change your hierarchy arbitrarily and expect queries to continue to work as expected - you may need to re-save() all affected entities to rewrite the indexed field.
Note that classes without the annotation (ie, the base type) will not have these synthetic properties. They will be treated as the base type.
There are two ways you can affect this:
# You can control indexing of subclasses by specifying {{{@Subclass(index=true)}}}. *Subclasses are not indexed by default*, you must explicitly enable this for each subclass. Note that this only affects queries; you can always get-by-key or get-by-id and receive the proper typed object irrespective of indexing.
# You can use {{{@Subclass(alsoLoad="OldDiscriminator")}}} to "reclaim" old discriminator values when changing class names. Note that this will not help with query indexes, which must be re-saved().
An alternative way to embed complex structures is to , which will let you store nearly any Java object graph.
There are some limitations:
* All objects stored in the graph must follow Java serialization rules, including implement {{{java.io.Serializable}}}.
* The total size of an entity cannot exceed 1 megabyte. If your serialized data exceeds this size, you will get an exception when you try to {{{save()}}} it.
* You will not be able to use the field or any child fields in queries.
* As per serializaton rules, {{{transient}}} (the java keyword) fields will not be stored.
* All Objectify annotations will be ignored within your serialized data structure. This means {{{@Ignore}}} fields within your serialized structure will be stored!
* Java serialization data is opaque to the datastore viewer and other languages (ie GAE/Python). You will only be able to retrieve your data from Java.
However, there are significant benefits to storing data this way:
* You can store nearly any object graph, including circular object references. If Java can serialize it, you can store it.
* Your field need not be statically typed. Declare {{{Object}}} if you want.
* Collections can be stored in their full state; for example, a SortedSet will remember its Comparator implementation.
You are *strongly* advised to place on all classes that you intend to store as . Without this, *any* change to your classes will prevent stored objects from being deserialized on fetch. Example:
You can automatically zip the serialized blob:
You can add or remove zipping at any time. Irrespective of the value, Objectify will read the field correctly whether it was zipped or not in the datastore. Of course, when the entity is saved, the value will determine whether or not the data in that instance is compressed.
A relationship is simply a key stored as a field in an entity. Objectify provides several facilities to make it easier to manage relationships using keys.
There are two different Key classes in Objectify:
# {{{com.google.appengine.api.datastore.Key}}}, usually referred to as {{{Key}}}
# {{{com.googlecode.objectify.Key}}}, usually referred to as {{{Key<?>}}}
- 1 is the native datastore class used by the low-level API. It is the "one true Key" because all datastore operations use this type; Objectify always converts to (or from) the native . However, because this structure lacks generic type information, it should almost never appear in your code.
- 2 is Objectify's generified version of . It's much less errorprone to use or , so you should always use when you use keys directly.
You can change the type of the field at any time, even after data is stored. Remember, the datastore contains the native .
Even s are not very convenient when you are working with graphs of entities. Objectify provides , which works just like a but allows you to directly access the actual entity object as well:
fields are stored as native fields in the datastore. You can freely swap between , , and in your Java data model without modifying stored data.
This provides a nice consistent structure for your object graph, but it's still clunky. works best in concert with the annotation.
In the above example, the caused an immediate fetch from the datastore. This could be a performance problem if you are iterating through lots of Cars loading their drivers; each driver fetch would be a separate request. By using the annotation, you can load drivers into the session efficiently:
Objectify will translate this into an _optimal_ number of batch fetches. For example:
This will produce one round of batch fetching for the cars, plus one round of batch fetching for all the drivers, plus one round of fetching for any fields in all the drivers... and on. Objectify is also smart about not re-fetching anything that has already been fetched in an earlier cycle. This optimized behavior is very hard to write by hand.
Often you will not wish to every entity relationship every time. provides the ability to specify conditional _load groups_. See Load Groups for more.
You will generally find it convenient to encapsulate behind getter and setter methods:
is a reference to a key, but does not itself hold an instance to an entity. s link to the current session; calling loads the referenced entity out of the session (loading from the datastore if necessary). Think of them as "live" connections to the datastore; if you load a , then the referenced entity, the next call to will return null.
There is an exception to this: If you serialize a (either with Java serialization or GWT-RPC), it becomes a "dead" holder of the referenced entity.
Parent relationships are described in [Concepts]. Objectify models them with the annotation:
Each Car entity is part of the parent owner's entity group. Important things to note:
* There can only be one {{{@Parent}}} field in a class!
* Along with the {{{@Id}}} and kind, the {{{@Parent}}} field defines the key (identity) of the entity.
* Construct a key to the car like this: {{{Key.create(ownerKey, Car.class, carId)}}}
* The name of the field is irrelevant and can be changed at any time without modifying stored data.
* The {{{@Parent}}} field can be {{{Key}}}, {{{Key<?>}}}, or {{{Ref<?>}}}.
* @Load annotations work with {{{Ref<?>}}}s. As a bonus, the parent will be loaded in the same batch as the initial load.
Note that this example is an inappropriate use of the @Parent entity; if a car were to be sold to a new owner, you would need to delete the Car and create a new one. It is often better to use simple nonparent key references even when there is a conceptual parent-child or owner-object relationship; in that case you could simply change the owner.
- VERY IMPORTANT*: *If you load() an entity, change the field, and save() the entity, you will create a new entity*. The old entity (with the old parent) will still exist. You cannot simply change the value of a field. This is a fundamental aspect of the appengine datastore; values form part of an entity's identity.
The datastore can natively persist collections of simple types, including keys. This creates one approach for defining one-to-many (and many-to-many) relationships.
This works with s, s, and s. You can use annotations with s.
There are some considerations:
* Every time you {{{load()}}} or {{{save()}}} an Employee, it will fetch and store the entire list of _subordinates_ keys. Large numbers of subordinates will bloat the entity and impact performance.
* App Engine limits you to 5,000 entries in a multi-valued property.
* Because fetches are performed by key, you have a strongly-consistent view of the a manager and subordinates.
If you put on _subordinates_, you can issue a query like this:
Storing keys in a collection field is a very "appenginey" way of modeling data, and faithfully works with the key/value nature of the datastore. However, this is not the traditional way of modeling one-to-many relationships in a relational database. You can still use the RDBMS approach, but there are caveats:
* This is a query and therefore weakly consistent. * You cannot use this query inside of a transaction. * Queries are less cache-friendly than direct key references.
The decision of which approach to take will depend heavily on the shape of your data and what queries you need to perform.
Before you use Objectify to load or save data, you must register all entity classes for your application. Objectify will introspect these classes and their annotations to build a metamodel which is used to efficiently manipulate entities at runtime.
* Registration must be done at application startup, before Objectify is used.
* Registration must be single-threaded. Do not register() from multiple threads.
* All entity classes must be registered, including polymorphic subclasses.
* {{{@Embed}}} classes do not need to be registered.
We recommend that you register classes in a static initializer for something that is used at application startup. For example:
Because this registration process requires loading and introspecting classes, it adds a small delay to your application start time which is directly proportional to the number of classes you register. While Objectify tries to minimize this delay by eschewing classpath-scanning, the delay is not entirely avoidable (classloading on appengine is very slow). The best that can be said is that this delay is significantly shorter than the equivalent JDO/JPA delay.