Annotation Interface PermazenField
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 mapkey
orvalue
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 a user-defined encoding, configure a custom EncodingRegistry
that knows about the encoding and
then refer to it by its unique EncodingId
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 either is 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
.
Reference Cascades
Reference cascades allow you to define an arbitrary graph of objects by specifying the reference fields that constitute
the edges of the graph. Reference cascades are identified by name, and a reference field is included in a reference cascade
when that name appears in forwardCascades()
and/or inverseCascades()
. Reference fields in reference cascades
can be traversed in either the forward or reverse directions.
For example, the PermazenObject
methods copyIn()
,
copyOut()
, and copyTo()
copy a graph of related objects between transactions by first copying a starting object and then cascading through matching
reference fields, repeating recursively. There is also PermazenTransaction.cascade()
which
performs a general purpose cascade exploration and returns the corresponding set of objects.
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);
}
}
References and Deletion
Reference fields have configurable behavior when the referring object or the referred-to object is deleted;
see forwardDelete()
and inverseDelete()
.
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
.
A uniqueness constraint applies to all objects that are instances of the class in which the field is declared. For example,
if classes Dog
and Cat
both implement Pet
, then a uniqueness constraint on a field declared in
Pet
would apply across all dogs and cats, whereas unique constraints on fields declared in Dog
and/or
Cat
would only apply to that specific animal. Note this remains true even when Dog
and Cat
declare
the same field; for example, if Dog
and Cat
both declare a "name"
field, then you could have two pets
with the same name, but only if one is a Dog
and one is a Cat
.
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 PermazenObject
s 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 immediate notice, or at least not 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.
-
Optional Element Summary
Modifier and TypeOptional ElementDescriptionboolean
Allow the field to reference non-existent objects in normal transactions.Specify the encoding for this field byEncodingId
URN.String[]
Define forward reference cascades for the annotated reference field.boolean
For reference fields, configure cascading behavior when the referring object is deleted.boolean
Whether this field is indexed or not.String[]
Define inverse find/copy cascades for the annotated reference field.For reference fields, configure the behavior when the referred-to object is deleted.The name of this field.int
Storage ID for this field.boolean
Require this field's value to be unique among all database objects.Specify field value(s) that should be excluded from the uniqueness constraint.Specify theUpgradeConversionPolicy
policy to apply when this field's type has changed due to a schema change.
-
Element Details
-
name
String nameThe 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 encodingSpecify the encoding for this field byEncodingId
URN.If set, this must equal the
EncodingId
of anEncoding
registered in theEncodingRegistry
associated with theDatabase
instance, and the annotated method's return type must match theEncoding
'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 viaEncodingRegistry.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; seeDefaultEncodingRegistry
for details.For reference fields, 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 storageIdStorage 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 indexedWhether 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[] forwardCascadesDefine 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.
Cascade names must be non-empty.
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[] inverseCascadesDefine 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 is encountered that is referred to through the annotated reference field, then the annotated reference field will will be traversed in the inverse direction.
Cascade names must be non-empty.
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 inverseDeleteFor 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 forwardDeleteFor 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 uniqueRequire 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 viamyobj.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 uniqueExcludesSpecify field value(s) that 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 bothnulls()
andnonNulls()
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 allowDeletedAllow 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 withDeleteAction.EXCEPTION
(seeinverseDelete()
), 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 toDeleteAction.IGNORE
.- Returns:
- whether the reference field should allow assignment to deleted objects in normal transactions
- See Also:
- Default:
- false
-
upgradeConversion
UpgradeConversionPolicy upgradeConversionSpecify theUpgradeConversionPolicy
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
value1234
to theString
value"1234"
).See
Encoding.convert()
for details about conversions between field encodings. In addition,Counter
fields may be automatically 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.Automatic upgrade conversion is only supported for plain simple fields. For sub-fields of complex fields, this property is ignored.
Note that arbitrary conversion logic is always possible using
@OnSchemaChange
methods.- Returns:
- upgrade conversion policy for this field
- See Also:
- Default:
- ATTEMPT
-