All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.permazen.annotation.OnSchemaChange Maven / Gradle / Ivy

The newest version!

/*
 * Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
 */

package io.permazen.annotation;

import io.permazen.PermazenCounterField;
import io.permazen.UntypedPermazenObject;
import io.permazen.UpgradeConversionPolicy;
import io.permazen.core.EnumValue;
import io.permazen.core.TypeNotInSchemaException;
import io.permazen.encoding.Encoding;
import io.permazen.schema.SchemaId;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Annotation for methods that are to be invoked whenever an object's schema has just changed,
 * in order to apply "semantic" schema migration logic.
 *
 * 
 * 
 * 
 *
 * 

* 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 * {@link UpgradeConversionPolicy}, {@link PermazenField#upgradeConversion @PermazenField.upgradeConversion()}, * and {@link Encoding#convert 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 {@code Map 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 {@link 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 {@code 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 {@link OnSchemaChange @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 {@link TypeNotInSchemaException}. Such objects are still accessible however (see {@link 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 {@link OnSchemaChange @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 {@link Enum} field refers to an {@link Enum} type that no longer exists, or whose identifiers have changed * (this is really just a special case of the previous scenario: when an {@link Enum} type's constants change * in any way, the new {@link Enum} is treated as a completely new type).
  • *
* *

* Therefore, the following special rules apply to the {@code oldValues} map: *

    *
  • For a reference field whose type no longer exists, the referenced object will appear as an {@link UntypedPermazenObject}. *
  • For {@link Enum} fields, old values are always represented as {@link EnumValue} objects. * For consistency's sake, this is true even if the associated field's type has not changed.
  • *
* *

* In addition, {@linkplain PermazenCounterField} values are represented in {@code oldValues} as values of type {@code Long}. * *

Meta-Annotations

* *

* This annotation may be configured indirectly as a Spring * meta-annotation * when {@code spring-core} is on the classpath. * * @see Encoding#convert Encoding.convert() * @see PermazenField#upgradeConversion @PermazenField.upgradeConversion() */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Documented public @interface OnSchemaChange { }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy