Annotation Interface PermazenField


@Retention(RUNTIME) @Target({ANNOTATION_TYPE,METHOD}) @Documented public @interface PermazenField
Java annotation for defining simple fields (including reference fields that refer to other Java model object types) and Counter fields.

This annotation is used in two scenarios:

  • To configure a simple or counter database field, by annotating the corresponding abstract Java bean property "getter" method
  • To configure the sub-field of a complex database field (i.e., set, list, or map), that is, a collection element field, or a map key or value field. In this case this annotation nests within the corresponding @PermazenListField, @PermazenSetField, or @PermazenMapField annotation.

When auto-generation of properties is enabled, use of this annotation is not required unless you want to override the defaults; see PermazenType.autogenFields().

The annotated method's declaring class does not have to be a @PermazenType-annotated type; annotations are "inherited" and so apply to all @PermazenType-annotated sub-types.

Reference Fields

If the type of the field is a @PermazenType-annotated Java model object type, or any supertype thereof, then the field is a reference field. Reference fields are simple fields that refer to other database objects.

Non-Reference Fields

If the field is not a reference field, the field's Java type is inferred from the type of the annotated method or, in the case of complex sub-fields, the generic type of the collection class. The field's Java type must be supported by some Encoding registered in the EncodingRegistry that is configured for the database so that the field's values can be encoded into byte[] array values in the key/value store.

See DefaultEncodingRegistry for a list of built-in (pre-defined) encodings.

To use user-defined encodings, configure a custom EncodingRegistry and specify them via encoding().

Referential Integrity

In general, reference fields may reference objects that don't actually exist. This can happen in one of two ways: (a) a field is set to an invalid reference, or (b) a field references a valid object that is subsequently deleted. The allowDeleted() and inverseDelete() properties, respectively, control whether (a) or (b) is permitted.

By default, neither (a) nor (b) is allowed; if attempted, a DeletedObjectException is thrown. This ensures references are always valid.

Indexing

Simple fields may be indexed by marking them as indexed(); see PermazenTransaction for information on querying indexes. This includes reference fields and fields with user-defined custom encodings.

Reference fields are always indexed.

Counter fields may not be indexed.

Two or more simple fields may be indexed together in a composite index; see @PermazenCompositeIndex.

Cascades

The PermazenObject methods copyIn(), copyOut(), and copyTo() copy a graph of related objects between transactions by first copying a starting object, then cascading through matching reference fields and repeating recursively. This cascade operation is capable of traversing references in both the forward and inverse directions. There is also PermazenTransaction.cascade(io.permazen.core.ObjId, int, io.permazen.core.util.ObjIdSet, java.lang.String...) which performs a general purpose cascade exploration.

Which reference fields are traversed in a particular find or copy operation is determined by the supplied cascade name. Outgoing references are traversed if the cascade name is in the reference field's forwardCascades() property, while incoming references from other objects are traversed (in the reverse direction) if the cascade name is in the referring object's reference field's inverseCascades().

For example:

  @PermazenType
  public interface TreeNode extends PermazenObject {

      /**
       * Get the parent of this node, or null if node is a root.
       */
      @PermazenField(forwardCascades = { "tree", "ancestors" }, inverseCascades = { "tree", "descendants" })
      TreeNode getParent();
      void setParent(TreeNode parent);

      /**
       * Get the children of this node.
       */
      @ReferencePath("<-TreeNode.parent")
      NavigableSet<TreeNode> getChildren();

      default TreeNode copySubtreeTo(PermazenTransaction dest) {
          return (TreeNode)this.cascadeCopyTo(dest, "descendants", false);
      }

      default TreeNode copyWithAnscestorsTo(PermazenTransaction dest) {
          return (TreeNode)this.cascadeCopyTo(dest, "ancestors", false);
      }

      default TreeNode copyEntireTreeTo(PermazenTransaction dest) {
          return (TreeNode)this.cascadeCopyTo(dest, "tree", false);
      }

      default TreeNode cloneEntireTreeTo(PermazenTransaction dest) {
          return (TreeNode)this.cascadeCopyTo(dest, "tree", true);
      }

      default TreeNode cloneEntireTree() {
          return (TreeNode)this.cascadeCopyTo(this.getTransaction(), "tree", true);
      }
  }
 

Delete Cascades

Reference fields have configurable behavior when the referring object or the referred-to object is deleted; see inverseDelete() and forwardDelete().

Uniqueness Constraints

Simple fields that are not complex sub-fields may be marked as unique() to impose a uniqueness constraint on the field. Uniqueness constraints function as an implicit validation constraint. In other words, the uniqueness constraint is verified when the validation queue is processed, and is affected by the transaction's configured ValidationMode.

Optionally, specific values may be marked as excluded from the uniqueness constraint via uniqueExcludes(). If so, the specified values may appear in more than one object without violating the constraint.

In ValidationMode.AUTOMATIC, any upgraded PermazenObjects are automatically added to the validation queue, so a uniqueness constraint added in a new schema will be automatically verified when any object is upgraded.

Beware however, that like most other types of validation constraint, adding or changing a uniqueness constraint can cause valid database objects to become invalid without notice, or at least until if/when the object is revalidated in some future transaction. In such a situation, you can force a schema change - and therefore revalidation on the next access - by incrementing PermazenType.schemaEpoch() in the field's containing type.

Upgrade Conversions

When a field's type has changed in a new schema, the old field value can be automatically converted into the new type. See upgradeConversion() for how to control this behavior.

Meta-Annotations

This annotation may be configured indirectly as a Spring meta-annotation when spring-core is on the classpath.

  • Element Details

    • name

      String name
      The name of this field.

      If equal to the empty string (default value), the name is inferred from the name of the annotated Java bean getter method.

      For sub-fields of complex fields, this property must be left unset.

      Returns:
      the name of the field
      Default:
      ""
    • encoding

      String encoding
      Specify the encoding for this field by EncodingId URN.

      If set, this must equal the EncodingId of an Encoding registered in the EncodingRegistry associated with the Database instance, and the annotated method's return type must match the Encoding's supported Java type.

      If this is left unset (empty string), then the Java type is inferred from the return type of the getter method and the Encoding is found via EncodingRegistry.getEncoding(TypeToken).

      For any of Permazen's built-in types, the Permazen URN prefix "urn:fdc:permazen.io:2020:" may be omitted. Otherwise, see EncodingId for the required format. Custom encodings can be found automatically on the application class path; see DefaultEncodingRegistry for details.

      For reference fields (i.e., methods with return value equal to a @PermazenType-annotated class), this property must be left unset.

      For sub-fields of complex fields, this property can be used to force a primitive type instead of a primitive wrapper type. In that case, the complex field will disallow null values. For example:

        @PermazenSetField(element = @PermazenField(type = "float")) // nulls will be disallowed
        public abstract List<Float> getScores();
       
      Returns:
      URN identifying the field's encoding
      See Also:
      Default:
      ""
    • storageId

      int storageId
      Storage ID for this field.

      Normally this value is left as zero, in which case a value will be automatically assigned.

      Otherwise, the value should be positive and unique within the contained class.

      Returns:
      the field's storage ID, or zero for automatic assignment
      Default:
      0
    • indexed

      boolean indexed
      Whether this field is indexed or not.

      Setting this property to true creates a simple index on this field. To have this field participate in a composite index on multiple fields, use @PermazenCompositeIndex.

      Note: reference fields are always indexed (for reference fields, this property is ignored).

      Returns:
      whether the field is indexed
      See Also:
      Default:
      false
    • forwardCascades

      String[] forwardCascades
      Define forward reference cascades for the annotated reference field.

      When following a cascade of references, if the cascade name is one of the names listed here, and an object with the annotated reference field is encountered, then the annotated reference field will will be traversed in the forward direction.

      For non-reference fields this property must be equal to its default value.

      Returns:
      forward cascade names for the annotated reference field
      See Also:
      Default:
      {}
    • inverseCascades

      String[] inverseCascades
      Define inverse find/copy cascades for the annotated reference field.

      When following a cascade of references, if the cascade name is one of the names listed here, and an object with the annotated reference field is encountered, then the annotated reference field will will be traversed in the inverse direction.

      For non-reference fields this property must be equal to its default value.

      Returns:
      inverse cascade names for the annotated reference field
      See Also:
      Default:
      {}
    • inverseDelete

      DeleteAction inverseDelete
      For reference fields, configure the behavior when the referred-to object is deleted.

      For non-reference fields this property must be equal to its default value.

      Returns:
      desired behavior when a referenced object is deleted
      See Also:
      Default:
      EXCEPTION
    • forwardDelete

      boolean forwardDelete
      For reference fields, configure cascading behavior when the referring object is deleted. If set to true, the referred-to object is automatically deleted as well.

      For non-reference fields this property must be equal to its default value.

      Returns:
      whether deletion should automatically propagate to the referred-to object
      See Also:
      Default:
      false
    • unique

      boolean unique
      Require this field's value to be unique among all database objects.

      This property creates an implicit uniqueness validation constraint.

      The constraint will be checked any time normal validation is performed on an object containing the field. More precisely, a uniqueness constraint behaves like a JSR 303 validation constraint with groups() = { Default.class, UniquenessConstraints.class }. Therefore, uniqueness constraints are included in default validation, but you can also validate only uniqueness constraints via myobj.revalidate(UniquenessConstraints.class).

      This property must be false for sub-fields of complex fields, and for any field that is not indexed.

      For reference fields, a uniqueness constraint enforces a one-to-one relationship.

      Returns:
      whether the field's value should be unique
      See Also:
      Default:
      false
    • uniqueExcludes

      Values uniqueExcludes
      Specify field value(s) which should be excluded from the uniqueness constraint.

      Examples:

        // Exclude objects with null names from the uniqueness constraint
        @PermazenField(indexed = true, unique = true, uniqueExcludes = @Values(nulls = true))
        public abstract String getName();
      
        // Exclude objects with non-finite priorities from the uniqueness constraint
        @PermazenField(indexed = true, unique = true, uniqueExcludes = @Values({ "Infinity", "-Infinity", "NaN" }))
        public abstract float getPriority();
       

      Use of @Values(nonNulls = true) would be somewhat unusual; that would mean there can be at most one object with a null value in the field, but otherwise there are no restrictions. Specifying both nulls() and nonNulls() generates an error, as that would exclude every object from the constraint, rendering it pointless.

      This property must be left empty when unique() is false.

      Returns:
      field values to be excluded from the uniqueness constraint
      See Also:
      Default:
      @io.permazen.annotation.Values
    • allowDeleted

      boolean allowDeleted
      Allow the field to reference non-existent objects in normal transactions.

      For non-reference fields, this property must be equal to its default value.

      Otherwise, if this property is set to false, the field is disallowed from ever referring to a non-existent object; instead, a DeletedObjectException will be thrown. When used together with DeleteAction.EXCEPTION (see inverseDelete()), the field is guaranteed to never be a dangling reference.

      This property only applies to regular (non-detached) transactions.

      For consistency, this property must be set to true when inverseDelete() is set to DeleteAction.IGNORE.

      Returns:
      whether the reference field should allow assignment to deleted objects in normal transactions
      See Also:
      Default:
      false
    • upgradeConversion

      UpgradeConversionPolicy upgradeConversion
      Specify the UpgradeConversionPolicy policy to apply when this field's type has changed due to a schema change.

      Permazen supports schema changes that alter a field's type, and in some cases can automatically convert field values from the old to the new type (for example, from the int value 1234 to the String value "1234").

      See Encoding.convert() for details about conversions between simple encodings. In addition, Counter fields can be converted to/from any numeric Java primitive (or primitive wrapper) type.

      This property defines the UpgradeConversionPolicy for the annotated field when upgrading an object from some other schema to the current schema. Note custom conversion logic is also possible using @OnSchemaChange methods.

      For sub-fields of complex fields, this property is ignored.

      Returns:
      upgrade conversion policy for this field
      See Also:
      Default:
      ATTEMPT