Persistence APIs
As editors create, modify, and delete items, Brightspot initiates corresponding database save and delete life cycles. You can override callbacks in these life cycles to provide additional features, such as sending confirmation or error messages to the UI.
As editors create and modify items, Brightspot makes corresponding changes in the database. You can override callbacks to provide additional functionality during the save life cycle. For example, when Brightspot successfully saves an item, you can display a notification in the content edit form.
Understanding the save life cycle
The save life cycle starts with either of the following methods:
- Record#save—Calling this method performs a standard save.
- Record#saveImmediately—Calling this method saves the object outside the context of a transaction.
The following diagram shows the various callbacks you can use during this life cycle.
Referring to the previous diagram—
- The available callbacks
beforeSave
,onValidate
,beforeCommit
,onDuplicate
, andafterSave
have empty implementations inRecord
, so you can override them in your own classes. save
andsaveImmediately
are not available callbacks, and cannot be overridden.
Record#saveUnsafely
method.
Save life cycle callbacks
This section describes the various callbacks during the extended save life cycle. These callbacks correspond to the diagram "Save life cycle."
saveImmediately
or deleteImmediately
. If you need to save or delete multiple objects, consider using a database transaction (see Transactions) with Database#beginIsolatedWrites.
beforeSave
Use beforeSave
for simple state changes and low-overhead database or API calls. In particular, you can use this method to modify data before the validation step in the save life cycle. One common use for beforeSave
is to populate hidden fields. Because editors cannot directly populated these fields in the UI, you must populate them in code.
import com.psddev.cms.db.Content;
import com.psddev.cms.db.ToolUi;
public class Article extends Content {
private String name;
private String code;
/* Declare a hidden field. */
@ToolUi.Hidden
private String internalName;
public String getName() {
return name;
}
public String getCode() {
return code;
}
/* Populate a hidden field. */
@Override
public void beforeSave() {
this.internalName = getName() + "-" + getCode();
}
}
onValidate
Use onValidate
for custom validation on an object beyond what the database or annotations supply, such as sending customized messages to the UI.
In the following snippet, the name
field is annotated with @Recordable.Required. If an editor saves an item with a blank name
, Brightspot displays a standard error message. The snippet overrides the onValidate
callback to display a custom error message.
import com.psddev.cms.db.Content;
public class Article extends Content {
@Required
private String name;
/* Display custom error message. */
protected void onValidate() {
if (name == null) {
getState().addError(getState().getField("name"), "Enter a name.");
}
}
}
Error messages for an empty required field
See also:
beforeCommit
Use beforeCommit
to make additional changes to the object that do not require validation. Examples include ensuring certain fields are null, prepending or appending strings, or populating fields with valid non-null values.
In the following example, the beforeCommit
method sets the lastUpdated
field to the current date and time.
import com.psddev.cms.db.Content;
import com.psddev.cms.db.ToolUi;
public class Article extends Content {
@ToolUi.Hidden
private Date lastUpdated;
protected void beforeCommit() {
lastUpdated = new Date();
}
}
onDuplicate
Use onDuplicate
or provide custom logic when the database save operation fails because a uniqueness violation is detected on an indexed field or method. The most common uses of this method are the following:
- Resolve the violation by setting a unique value and return
true
. In this case the save life cycle returns to theonValidate
method. - Display a custom message in the content edit form and return
false
. In this case the save life cycle returns to thebeforeSave
method.
In the following example, the headline
field is annotated with @Indexed(unique = true)
. If an editor saves an item with an existing headline
, Brightspot displays a standard error message. The following snippet overrides the onDuplicate
callback to display a custom error message.
import com.psddev.cms.db.Content;
import com.psddev.dari.db.ObjectIndex;
public class Article extends Content {
@Indexed(unique = true)
private String headline;
/* Display custom error message. */
protected boolean onDuplicate(ObjectIndex index) {
getState().addError(getState().getField("headline"), "Another article has this headline. Use a different headline.");
return false;
}
}
Error messages for duplicate values
See also:
@Recordable.Indexed
afterSave
Use afterSave
to provide processing after the database save, typically for sending messages to the UI or to log files. The following snippet overrides the afterSave
callback to write a message to stdout.
import com.psddev.cms.db.Content;
public class Article extends Content {
private String headline;
public String getHeadline() {
return headline;
}
/* Write message to stdout. */
protected void afterSave() {
System.out.println("Saved an article with the following headline: " + getHeadline());
}
}
27-Feb-2020 14:32:06 INFO Saved an article with the following headline: After 300 years, Astronaut Higgins escapes from black hole. Doesn't look a day older.
As editors delete items in the UI, Brightspot deletes corresponding records in the database. You can override callbacks to provide additional functionality during the delete life cycle. For example, when Brightspot successfully deletes an item, you can write a message to a log file.
Understanding the delete life cycle
The delete save life cycle starts with the Record#delete
method. The following diagram shows the various callbacks you can use during this life cycle.
Referring to the previous diagram—
- The available callbacks
beforeDelete
andafterDelete
have empty implementations inRecord
, so you can override them in your own classes. delete
is not an available callback, and cannot be overridden.
Delete life cycle callbacks
This section describes the various callbacks during the delete life cycle. These callbacks correspond to the diagram "Delete life cycle."
saveImmediately
or deleteImmediately
. If you need to save or delete multiple objects, consider using a transaction (see Transactions) with Database#beginIsolatedWrites.
beforeDelete
Use beforeDelete
to provide validation, referential integrity, or messaging before actually deleting an object. The following snippet deletes all of an author’s articles from the database before deleting the author.
import com.psddev.cms.db.Content;
import com.psddev.dari.db.Query;
public class Author extends Content {
/* Delete all of an author's articles before actually deleting the author. */
@Override
protected void beforeDelete() {
Query.from(Article.class).where("author = ?", this.getId()).deleteAll();
}
}
afterDelete
Use afterDelete
to provide processing after the database delete, typically for sending messages to the UI or to log files. The following snippet overrides the afterDelete
callback to write a message to stdout.
import com.psddev.cms.db.Content;
public class Author extends Content {
@Override
protected void afterDelete() {
System.out.println("Deleted an author with ID " + this.getId());
}
}
27-Feb-2020 15:41:38 INFO Deleted an author with ID 00000170-8861-d763-a7f7-fff91cee0000