Annotation Interface OnSchemaChange
The annotated method is given access to all of the values in the previous object version's fields, including for fields that have been removed or changed types. This allows the object to perform any schema migration "fixups" that may be required before the old information is lost for good.
Simple changes that only modify a simple field's type can often be handled automatically; see
UpgradeConversionPolicy
, @PermazenField.upgradeConversion()
,
and Encoding.convert()
for details.
The annotated method must be an instance method (i.e., not static), return void, and take from one to three parameters.
The first parameter must have type Map<String, Object> oldValues
and will be an immutable map containing the values
of all the fields in the previous schema version of the object indexed by field name. The optional second and third
parameters have type SchemaId
and identify the old and new schemas (respectively).
In many cases, the simplest way to handle schema changes is to use the presence or absence of fields in oldValues
to determine what migration work needs to be done. For example:
@OnSchemaChange
private void applySchemaChanges(Map<String, Object> oldValues) {
// At some point we added a new field "balance"
if (!oldValues.containsKey("balance"))
this.setBalance(this.calculateBalanceForSchemaMigration());
// At some point we replaced "fullName" with "lastName" & "firstName"
if (oldValues.containsKey("fullName")) {
final String fullName = (String)oldValues.get("fullName");
if (fullName != null) {
final int comma = fullName.indexOf(',');
this.setLastName(comma == -1 ? null : fullName.substring(0, comma));
this.setFirstName(fullName.substring(comma + 1).trim());
}
}
// ...etc
}
A class may have multiple @OnSchemaChange
-annotated methods.
Incompatible Object Type Changes
Permazen supports arbitrary Java model schema changes across schemas, including adding and removing Java types. This creates a few caveats relating to schema migration.
First, if an object's type no longer exists in the new schema, migration is not possible, and any attempt to do so will
throw a TypeNotInSchemaException
. Such objects are still accessible however (see UntypedPermazenObject
).
Secondly, it's possible for an old field to have a value that can no longer be represented within the new schema.
When this happens, it's not possible to provide the old value to an @OnSchemaChange
method
in its original form.
This can happen in two ways:
- A reference field refers to an object whose type no longer exists in the new schema; or
- An
Enum
field refers to anEnum
type that no longer exists, or whose identifiers have changed (this is really just a special case of the previous scenario: when anEnum
type's constants change in any way, the newEnum
is treated as a completely new type).
Therefore, the following special rules apply to the oldValues
map:
- For a reference field whose type no longer exists, the referenced object will appear as an
UntypedPermazenObject
. - For
Enum
fields, old values are always represented asEnumValue
objects. For consistency's sake, this is true even if the associated field's type has not changed.
In addition, PermazenCounterField values are represented in oldValues
as values of type Long
.
Meta-Annotations
This annotation may be configured indirectly as a Spring
meta-annotation
when spring-core
is on the classpath.