Jello uses Objectify as the underlying persistence framework to model relationships between persistent Entities. By following Jello guidelines for Relationship modeling as described below, you will also benefit from the complete REST representation support; including Deferred reference fetching, reference integrity validation, inline association count and Deferred Content Preview.
Calculated One-to-Many association
Strong Association Relationships
Inline Representation of Associated Entries
A reference relationship is simply a key stored as a field in an entity. Jello provides several facilities to make it easier to manage relationships using keys.
You can use the datastore raw key with @jello.annotation.Reference
annotation or Objectify generic Key
or Ref
.
Syntax:
1 2 3 4 5 6 7 8 9 10 | @Reference(<JelloEntity class>) com.google.appengine.api.datastore.Key rawKey; com.googlecode.objectify.Ref<JelloEntity> objectifyRef; com.googlecode.objectify.Key<JelloEntity> objectifyKey; // Obsolete. No reference data integrity validation will be performed @Reference(<JelloEntity class>) String ref; |
Example:
1 2 3 4 5 6 7 8 9 10 11 12 | public class Product extends JelloEntity { Key<Category> categoryKey; Ref<Category> categoryRef; @Reference(Category.class) public com.google.appengine.api.datastore.Key categoryRawKey; } public class Category extends JelloEntity { @KeyElement public Integer id; } |
Upon updating a reference field's value, Jello will validate the value to ensure Reference Integrity.
That is, the reference instance exists and is of the same type as the reference declared type.
Read the Deferred Content section to learn how Jello handles references representation via REST API.
/jello/meta/demo/Product { name: "demo.Product", ... fields: [ { name: "category", type: "jello.Reference(demo.Category)" }, ... } ... }
Either of the following formats can be use to pass reference value as a field value, action's argument, or query filter argument.
// Full path notation <entity path>(instance-id) // Key-only notation instance-id
Example: passing reference as query argument
/jello/view/demo.inventory/Product?$filter=category==demo.inventory.Category(Bike)
Validation to ensure Reference Integrity will be performed only if the full path notation is using.
Define 1:N calculated association field. This is a none-persistent but rather a calculated field. Jello performs select query and returns association list just upon request.
Syntax:
@Association([mappedBy="<field-name>"], [filteredBy="<query-select-expression>"])
Jello will execute a select query based on the mappedBy
and/or filteredBy
values.
If neither of these are defined, a strong association is assumed.
Strong association is a special case of managed association and has many implications on data persistence strategy and transactions.
See Strong Association for more details.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Category extends JelloEntity { @KeyElement public Integer id; @Expose @Association(mappedBy="category") public List<Product> products; @Expose @Association(mappedBy="category", filteredBy="price<99") public List<Product> cheapestInCategory; } public class Product extends JelloEntity { ... @Expose public Integer price; @Expose public Ref<Category> category; } |
jello.model.JelloEntity.getAssociations
public List<? extends JelloEntity> getAssociations(String fieldName) throws IllegalRequestResource
Performs select query and returns association list.
Read the Deferred Content section to learn how Jello handles associations representation via REST API.
/jello/meta/demo/Category { name: "demo.Category", ... fields: [ { name: "products", readOnly: true, type: "jello.Association(demo.Product)" }, ... } ... }
Reference field annotated with @groupElement
is a special type of key element used to establish strong association.
It also has implications on data persistence strategy and transactions. (Read about Transactions for more details)
When you delete an entity, Jello will also delete all the entities referring to that entity with strong reference.
Only one field annotated with @groupElement
can be defined per Entity.
Any class which is not recognized as a core value type will be broken down into its component fields and stored natively as an embedded structure. The fields of the object value are stored directly in the database entity of the containing object.
If the embedded class is itself a @JelloEntity
, all Jello annotations and lifecycle methods will honored.
However, embedded JelloEntity will not create a foreign key reference.
If you mark entity accessible as embedded only @Accessible(Role.AS_EMBEDDED_ONLY)
, the entity will not be accessible directly but only through the containing entity.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Supplier extends JelloEntity { @Expose @KeyElement Integer id; @Expose Address address; } @Accessible(Role.AS_EMBEDDED_ONLY) class Address extends JelloEntity { @Expose String street; @Expose String city; @Expose String state; @Expose String zipCode; @Expose String country; } |
Jello index embedded field by default as it does to all other fields. This allows you to use the dot notation to query on embedded fields:
http://localhost:8888/jello/data/demo/Supplier?$filter=address.street eq "1950 El Camino Real"
Fields of Embedded type will not be deferred but are always serialized fully.
You can add @Summary
UX annotation to the embedded field to hint the client how to render the field.
(see Summary annotation)
To conserve resources (bandwidth, CPU, DB calls, and so on), Jello will defer sending Reference and Associations entries unless the client explicitly asked for them using the $expand option. This provides a way for a client to state which associated entities should be represented inline.
As shown in the example below, by default properties that represent links (the "cast" and "director" properties in the example) are represented as an object with a "__deferred" name/value pair to indicate the service deferred representing the related Entries. The uri name/value pair within the "__deferred" object can be used to retrieve the deferred content.
Note: As shown in the example above, the deferred uri entry contains the entity's key.
The entity key is composed of all fields annotated with @KeyElement
and @GroupElement
. The entity's key will be visible via REST API
(as part of Deferred links uri) regardless of the element's accessibility definition.
In many cases, it is desired to display a sneak peek (preview) information for deferred references and associations. With Jello, you can specify a field element to be used as the preview content appended inline the deferred entry.
Syntax:
@Preview(element = "<preview-field-name>")
Here is an example of a preview field. This example makes use of a calculated field. Using a calculated field to render preview content can be useful, but not required for preview field.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class Casting extends JelloEntity { @Preview(element = "preview") @KeyElement @Expose Key<Actor> star; ... } public class Actor extends JelloEntity { @Expose @Attachment(accept="image/*") String photo; @Expose String name; @Expose private String preview; public String preview() throws IllegalRequestResource { String imgUrl = Util.getHostURL() + this.getPath() + "/photo/$value"; return "<img src='" + imgUrl + "' style='height:40px;'> " + name; } } |
As with any other jello.ux annotation, the preview metadata will be returned as part of the entity metadata. This information can be used by a client to display the appropriate references preview value.
You can use the $inlinecount REST query option to include Associations count as shown below.
As described in the $$expand Query Option section, a query request may include the $expand option to explicitly request that a linked to Entity or Associated collection of Entities be serialized inline, rather than deferred. For example, a single 'Movie' Instance with its related 'director' and 'cast' serialized inline as shown in the example below.
Was this page helpful? Let us know how we did:
Last updated March 12, 2019.