@Retention(value=RUNTIME) @Target(value={ANNOTATION_TYPE,METHOD}) @Documented public @interface JField
Counter
fields.
This annotation is used in two scenarios:
element
field, or a map key
or value
field. In this case this annotation
nests within the corresponding @JListField
, @JSetField
,
or @JMapField
annotation.
This annotation can be applied to superclass and interface methods to have the corresponding field defined in all
@PermazenType
-annotated sub-types.
When auto-generation of properties is enabled, use of this annotation is not required unless you need to override
the defaults; see PermazenType.autogenFields()
.
Non-Reference Fields
If the field is not a reference field, the property 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 name of the property type
must be registered in the FieldTypeRegistry
(perhaps via @JFieldType
),
and the corresponding FieldType
is then used to encode/decode field values.
See FieldTypeRegistry
for a list of built-in (pre-defined) field types.
The type name may also be specified explicitly by name()
.
Simple fields may be indexed()
; see io.permazen.index
for information on querying indexes.
Counter
fields may not be indexed.
Two or more simple fields may be indexed together in a composite index; see @JCompositeIndex
.
Reference Fields
If the type of the field is (assignable to) a @PermazenType
-annotated Java model object type,
or any supertype thereof, then the field is a reference field.
Reference fields are always indexed; the value of indexed()
is ignored.
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 onDelete()
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.
Copy Cascades
The JObject
methods cascadeCopyIn()
,
cascadeCopyOut()
, and cascadeCopyTo()
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.
Which reference fields are traversed in a particular copy operation is determined by the supplied cascade name.
Outgoing references are traversed if the cascade name is in the reference field's cascades()
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 JObject { /** * Get the parent of this node, or null if node is a root. */ @JField(cascades = { "tree", "ancestors" }, inverseCascades = { "tree", "descendants" }) TreeNode getParent(); void setParent(TreeNode parent); /** * Get the children of this node. */ @FollowPath(inverseOf = "parent", startingFrom = TreeNode.class) NavigableSet<TreeNode> getChildren(); default TreeNode copySubtreeTo(JTransaction dest) { return (TreeNode)this.cascadeCopyTo(dest, "descendants", false); } default TreeNode copyWithAnscestorsTo(JTransaction dest) { return (TreeNode)this.cascadeCopyTo(dest, "ancestors", false); } default TreeNode copyEntireTreeTo(JTransaction dest) { return (TreeNode)this.cascadeCopyTo(dest, "tree", false); } default TreeNode cloneEntireTreeTo(JTransaction 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 onDelete()
and cascadeDelete()
.
Uniqueness Constraints
Fields that are not complex sub-fields may be marked as unique()
to impose a uniqueness constraint on the value.
Fields with uniqueness constraints must be indexed. Uniqueness constraints are handled at the Permazen layer and function as
an implicit validation constraint. In other words, the constraint is verified when the validation queue is processed
and is affected by the transaction's configured ValidationMode
.
Optionally, specific field values may be marked as excluded from the uniqueness constraint via uniqueExclude()
.
If so, the specified values may appear in multiple objects without violating the constraint. Because null values
are not allowed in annotations, include NULL
to indicate that null values should be excluded.
In ValidationMode.AUTOMATIC
, any upgraded JObject
s are automatically
added to the validation queue, so a uniqueness constraint added in a new schema version will be automatically verified
when any object is upgraded.
Beware however, that like all other types of validation constraint, uniqueness constraints can be added or changed on a field without any schema version change. Therefore, after such changes, it's possible for pre-existing database objects that were previously valid to suddenly become invalid, and these invalid objects would not be detected until they are validated in some future transaction and a validation exception is thrown.
Upgrade Conversions
When a field's type has changed in a new schema version, 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.
Modifier and Type | Fields and Description |
---|---|
static String |
NULL
Value for use with
uniqueExclude() to represent a null value. |
Modifier and Type | Optional Element and Description |
---|---|
boolean |
allowDeleted
Allow the field to reference non-existent objects in normal transactions.
|
boolean |
allowDeletedSnapshot
Allow the field to reference non-existent objects in snapshot transactions.
|
boolean |
cascadeDelete
For reference fields, configure cascading behavior when the referring object is
deleted.
|
String[] |
cascades
Define forward copy cascades for the annotated reference field.
|
boolean |
indexed
Whether this field is indexed or not.
|
String[] |
inverseCascades
Define inverse copy cascades for the annotated reference field.
|
String |
name
The name of this field.
|
DeleteAction |
onDelete
For reference fields, configure the behavior when the referred-to object is
deleted.
|
int |
storageId
Storage ID for this field.
|
String |
type
Optional override for the type of this field.
|
long |
typeSignature
Optional override for the encoding signature
associated with this field's
FieldType used to encode/decode field values. |
boolean |
unique
Require this field's value to be unique among all database objects.
|
String[] |
uniqueExclude
Specify field value(s) which are excluded from the uniqueness constraint.
|
UpgradeConversionPolicy |
upgradeConversion
Specify the
UpgradeConversionPolicy policy to apply when a schema change occurs and this field's type changes. |
public static final String NULL
uniqueExclude()
to represent a null value.
Note: this particular String
will never conflict with any actual field values because it contains a character
that is not allowed in the return value from FieldType.toString()
.
public abstract String name
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.
public abstract String type
If set, this must equal the name of a type registered in the FieldTypeRegistry
associated with the Database
instance, and the annotated method's return type must match the
FieldType
's supported Java type.
If equal to the empty string (default value), then the Java type is inferred from the return type of the getter method
and the FieldType
is found via
FieldTypeRegistry.getFieldType()
.
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 sub-field type instead of a primitive wrapper type. In that case, the complex field will disallow null values. For example:
@JSetField(element = @JField(type = "float")) // nulls will be disallowed public abstract List<Float> getScores();
FieldType
,
FieldTypeRegistry.getFieldType(String, long)
public abstract long typeSignature
FieldType
used to encode/decode field values.FieldType
,
FieldTypeRegistry.getFieldType(String, long)
public abstract int storageId
Value should be positive and unique within the contained class.
If zero, the configured StorageIdGenerator
will be consulted to auto-generate a value
unless PermazenType.autogenFields()
is false (in which case an error occurs).
StorageIdGenerator.generateFieldStorageId()
,
StorageIdGenerator.generateSetElementStorageId()
,
StorageIdGenerator.generateListElementStorageId()
,
StorageIdGenerator.generateMapKeyStorageId()
,
StorageIdGenerator.generateMapValueStorageId()
public abstract boolean indexed
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 @JCompositeIndex
.
Note: reference fields are always indexed (for reference fields, this property is ignored).
@JCompositeIndex
public abstract String[] cascades
When JObject.cascadeCopyIn()
,
JObject.cascadeCopyOut()
, or
JObject.cascadeCopyTo()
is invoked, if the given cascade name is one
of the names listed here, and an object with the annotated reference field is copied, then the reference field will
will be traversed in the forward direction and the referred-to object will also be copied.
For non-reference fields this property must be equal to its default value.
JObject.cascadeCopyTo()
,
JTransaction.cascadeFindAll()
public abstract String[] inverseCascades
When JObject.cascadeCopyIn()
,
JObject.cascadeCopyOut()
, or
JObject.cascadeCopyTo()
is invoked, if the given cascade name is one
of the names listed here, and an object with the annotated reference field refers to an object that is copied, then the
reference field will be traversed in the inverse direction and the referring object will also be copied.
For non-reference fields this property must be equal to its default value.
JObject.cascadeCopyTo()
,
JTransaction.cascadeFindAll()
public abstract DeleteAction onDelete
For non-reference fields this property must be equal to its default value.
cascadeDelete()
,
JObject.delete()
public abstract boolean cascadeDelete
For non-reference fields this property must be equal to its default value.
onDelete()
,
JObject.delete()
public abstract boolean unique
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.
uniqueExclude()
,
UniquenessConstraints
public abstract String[] uniqueExclude
The specified values must be valid String
encodings of the associated field (as returned by
FieldType.toString(T)
), or the constant NULL
to indicate a null value. For example:
@JField(indexed = true, unique = true, uniqueExclude = { "Infinity", "-Infinity" }) public abstract float getPriority(); @JField(indexed = true, unique = true, uniqueExclude = { JField.NULL }) public abstract String getName();
This property must be left empty when unique()
is false.
unique()
public abstract boolean allowDeleted
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 onDelete()
), the field is guaranteed to never be a dangling reference.
This property only controls validation in regular (non-snapshot transactions); allowDeletedSnapshot()
separately controls validation for SnapshotJTransaction
s.
For consistency, this property must be set to true when onDelete()
is set to DeleteAction.NOTHING
.
onDelete()
,
allowDeletedSnapshot()
,
PermazenType.autogenAllowDeleted()
public abstract boolean allowDeletedSnapshot
For non-reference fields, this property must be equal to its default value.
This property is equivalent to allowDeleted()
, but applies to SnapshotJTransaction
s
instead of normal JTransaction
s; see allowDeleted()
for details.
Snapshot transactions typically hold a copy of some small portion of the database. If this property is set to false, then it effectively creates a requirement that this "small portion" be transitively closed under object references.
For consistency, this property must be set to true when onDelete()
is set to DeleteAction.NOTHING
.
onDelete()
,
allowDeleted()
,
PermazenType.autogenAllowDeletedSnapshot()
public abstract UpgradeConversionPolicy upgradeConversion
UpgradeConversionPolicy
policy to apply when a schema change occurs and this field's type changes.
With one restriction*, 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 FieldType.convert()
for details about conversions between simple field types.
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 version to the current schema version. Note custom conversion logic is also possible using
@OnVersionChange
methods.
For sub-fields of complex fields, this property is ignored.
*A simple field may not have different types across schema versions and be indexed in both versions.
UpgradeConversionPolicy
,
FieldType.convert()
,
OnVersionChange
Copyright © 2022. All rights reserved.