Documentation / Features / Draftable
Overview
Draftable is a feature that uses a second set of tables to hold a 'draft' version of the object graph separately from the 'live'. Typically the draft objects are edited and go through an approval process before being published.
To implement this feature all @Draftable and @DraftElement beans have a second draft table that closely resembles the live table but commonly will have additional columns to support an approval workflow for publishing.
Publishing is a function that takes underlying rows from the draft tables and copies them to the matching live table. In this way the application can easily maintain a 'draft' version and 'live' version of the entities.
Mapping
ENTITY MAPPING
@Draftable
@Draftable
is an annotation put on entity beans that should support draftable
features.
The @Draftable annotation put on 'top level' (or root level) entity beans and @DraftableElement is put on related child entity beans which are considered part of the same graph.
Publishing a @Draftable
bean will publish the 'top level' @Draftable entity beans
along with any of its related child @DraftableElement beans.
@DraftableElement
@DraftableElement
is an annotation put on entity beans that are part of a draftable
object graph but not 'top level' beans. The publish() and draftRestore() functions act on a draftable
bean and all it's associated draftableElement beans as a single unit (published/restored as a unit).
PROPERTY MAPPING
@DraftOnly
@DraftOnly
is an annotation put on properties that exist on the draft table only
- these properties do not exist on the associated live table. For example, annotation properties
that are on the draft to support approval workflow (workflow status, when publish timestamp etc).
@DraftDirty
@DraftDirty
is an annotation that can be put on a boolean property of a @Draftable entity bean.
This property only exists on the draft table and it's value is automatically set to true when the draft
bean is saved and automatically set to false when the draft bean is published. The property is expected
to be used to identify (query) draft beans that should be published.
Currently a change to a @DraftableElement does not set the dirty flag on the 'owning' @Draftable bean and instead this would need to be done manually in this case if required.
@DraftReset
@DraftReset
is an annotation that can be put on a property of a @Draftable entity bean.
The value of this property is automatically set to null on the draft bean after it has been published.
For example, the property is could contain comments or timestamp values related to the approval workflow
(and after a bean is publish these are 'reset' to null on the draft bean).
Query As Draft
A normal Query builds the object graph from the 'live' tables. You can specify the query to run
asDraft()
and then it will build the object graph using the draft tables. This can be
used to PREVIEW
the currently editing state of the object graphs.
// Get the 'draft' object graph
Document documentDraft =
Ebean.find(Document.class)
.setId(docId)
.asDraft()
.findOne();
// Get the 'draft' documents
List<Document> draftDocuments =
Ebean.find(Document.class)
.where()
.eq("dirty", true)
.ge("whenPublish", now)
.asDraft()
.findList();
Note: The asDraft
query state is propagated to any lazy loading or query joins.
Note: Any asDraft
query does not use the L2 cache (Only queries for live beans can use L2 cache).
Publish
Publish
is the function that takes the values from draft object graph and applies them
to the matching live object graph. The publish function cascades from a top level @Draftable entity bean
to any related @DraftableElement entity beans.
@OneToMany
A @OneToMany
relationship to a @DraftableElement effectively has save and delete cascade
turned on automatically. The cascade save/delete is used internally to publish the object graph.
@ManyToMany
A @ManyToMany
relationship to a @Draftable bean effectively has save and delete cascade
turned on automatically to maintain the relationship.
Database database = DB.getDefault();
// publish a single bean (from draft to live)
// returning the 'live' bean
Document liveDoc = database.publish(Document.class, docId);
// publish using a query
Query<Link> pubQuery = database.find(Link.class)
.where().idIn(ids)
.order().asc("id");
// publish returning the resulting 'live' beans
List<Link> pubList = database.publish(pubQuery);
Draft Restore
DraftRestore
is the function that takes the values from the live object graph and applies them
back to the matching draft object graph. You can consider it the opposite of a publish().
Database database = DB.getDefault();
// Restore a single draft bean
database.draftRestore(Document.class, docId);
Query<Document> restoreQuery = database.find(Document.class)
.where().idIn(ids)
.order().asc("id");
// Restore all the beans matching a query
database.draftRestore(restoreQuery);
Example Application
An example application is available at example-draftable .
L2 Cache
Only live beans and queries can use the L2 cache. Similarly only save/delete of live beans invalidate parts of the L2 cache. All queries for draft beans do not use the L2 cache and save/delete of draft beans do not invalidate any part of the L2 cache.