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

org.jsimpledb.annotation.JField Maven / Gradle / Ivy

The newest version!

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

package org.jsimpledb.annotation;

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;

import org.jsimpledb.UpgradeConversionPolicy;
import org.jsimpledb.core.DeleteAction;

/**
 * Java annotation for defining simple fields, including reference fields that refer to other Java model object types,
 * and {@link org.jsimpledb.Counter} fields.
 *
 * 

* This annotation is used in two scenarios: *

    *
  • To describe a simple or counter database field by annotating the corresponding abstract Java bean * property `getter' method
  • *
  • To describe the sub-field of a complex database field (i.e., set, list, or map), that is, * a collection {@code element} field, or a map {@code key} or {@code value} field. In this case this annotation * nests within the corresponding {@link JListField @JListField}, {@link JSetField @JSetField}, * or {@link JMapField @JMapField} annotation.
  • *
* *

* This annotation can be applied to superclass and interface methods to have the corresponding field defined in all * {@link JSimpleClass @JSimpleClass}-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 {@link JSimpleClass#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 {@link org.jsimpledb.core.FieldTypeRegistry} (perhaps via {@link JFieldType @JFieldType}), * and the corresponding {@link org.jsimpledb.core.FieldType} is then used to encode/decode field values. * See {@link org.jsimpledb.core.FieldTypeRegistry} for a list of built-in (pre-defined) field types. * The type name may also be specified explicitly by {@link #name}. * *

* Simple fields may be {@link #indexed}; see {@link org.jsimpledb.index} for information on querying indexes. * {@link org.jsimpledb.Counter} fields may not be indexed. * *

* Two or more simple fields may be indexed together in a composite index; see {@link JCompositeIndex @JCompositeIndex}. * *

Reference Fields

* *

* If the type of the field is (assignable to) a {@link JSimpleClass @JsimpleClass}-annotated Java model object type, * or any supertype thereof, then the field is a reference field. * *

* Reference fields are always indexed; the value of {@link #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 {@link #allowDeleted} and {@link #onDelete} properties, respectively, control whether (a) or (b) is permitted. * *

* By default, neither (a) nor (b) is allowed; if attempted, a {@link org.jsimpledb.core.DeletedObjectException} is thrown. * This ensures references are always valid. * *

Copy Cascades

* *

* The {@link org.jsimpledb.JObject} methods {@link org.jsimpledb.JObject#cascadeCopyIn cascadeCopyIn()}, * {@link org.jsimpledb.JObject#cascadeCopyOut cascadeCopyOut()}, and {@link org.jsimpledb.JObject#cascadeCopyTo 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 {@link #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 {@link #inverseCascades}. * *

* For example: *

 *  @JSimpleClass
 *  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 {@link #onDelete} and {@link #cascadeDelete}. * *

Uniqueness Constraints

* *

* Fields that are not complex sub-fields may be marked as {@link #unique} to impose a uniqueness constraint on the value. * Fields with uniqueness constraints must be indexed. Uniqueness constraints are handled at the JSimpleDB 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 {@link org.jsimpledb.ValidationMode}. * *

* Optionally, specific field values may be marked as excluded from the uniqueness constraint via {@link #uniqueExclude}. * If so, the specified values may appear in multiple objects without violating the constraint. Because null values * are not allowed in annotations, include {@link #NULL} to indicate that null values should be excluded. * *

* In {@link org.jsimpledb.ValidationMode#AUTOMATIC}, any upgraded {@link org.jsimpledb.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 {@link #upgradeConversion} for how to control this behavior. * *

Meta-Annotations

* *

* This annotation may be configured indirectly as a Spring * meta-annotation * when {@code spring-core} is on the classpath. */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Documented public @interface JField { /** * Value for use with {@link #uniqueExclude} to represent a null value. * *

* Note: this particular {@link String} will never conflict with any actual field values because it contains a character * that is not allowed in the return value from {@link org.jsimpledb.core.FieldType#toString(Object) FieldType.toString()}. */ String NULL = "\u0000"; /** * 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. * * @return the name of the field */ String name() default ""; /** * Optional override for the type of this field. * *

* If set, this must equal the name of a type registered in the {@link org.jsimpledb.core.FieldTypeRegistry} * associated with the {@link org.jsimpledb.core.Database} instance, and the annotated method's return type must match the * {@link org.jsimpledb.core.FieldType}'s {@linkplain org.jsimpledb.core.FieldType#getTypeToken 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 {@link org.jsimpledb.core.FieldType} is found via * {@link org.jsimpledb.core.FieldTypeRegistry#getFieldType(com.google.common.reflect.TypeToken) * FieldTypeRegistry.getFieldType()}. * *

* For reference fields (i.e., methods with return value equal to a {@link JSimpleClass @JSimpleClass}-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();
     * 
* * @return the name of the field's type * @see org.jsimpledb.core.FieldType * @see org.jsimpledb.core.FieldTypeRegistry#getFieldType(String, long) */ String type() default ""; /** * Optional override for the {@linkplain org.jsimpledb.core.FieldType#getEncodingSignature encoding signature} * associated with this field's {@link org.jsimpledb.core.FieldType} used to encode/decode field values. * * @return the encoding signature of the field's type * @see org.jsimpledb.core.FieldType * @see org.jsimpledb.core.FieldTypeRegistry#getFieldType(String, long) */ long typeSignature() default 0; /** * Storage ID for this field. * *

* Value should be positive and unique within the contained class. * *

* If zero, the configured {@link org.jsimpledb.StorageIdGenerator} will be consulted to auto-generate a value * unless {@link JSimpleClass#autogenFields} is false (in which case an error occurs). * * @see org.jsimpledb.StorageIdGenerator#generateFieldStorageId StorageIdGenerator.generateFieldStorageId() * @see org.jsimpledb.StorageIdGenerator#generateSetElementStorageId StorageIdGenerator.generateSetElementStorageId() * @see org.jsimpledb.StorageIdGenerator#generateListElementStorageId StorageIdGenerator.generateListElementStorageId() * @see org.jsimpledb.StorageIdGenerator#generateMapKeyStorageId StorageIdGenerator.generateMapKeyStorageId() * @see org.jsimpledb.StorageIdGenerator#generateMapValueStorageId StorageIdGenerator.generateMapValueStorageId() * * @return the field's storage ID */ int storageId() default 0; /** * 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 {@link JCompositeIndex @JCompositeIndex}. * *

* Note: reference fields are always indexed (for reference fields, this property is ignored). * * @return whether the field is indexed * @see JCompositeIndex @JCompositeIndex */ boolean indexed() default false; /** * Define forward copy cascades for the annotated reference field. * *

* When {@link org.jsimpledb.JObject#cascadeCopyIn JObject.cascadeCopyIn()}, * {@link org.jsimpledb.JObject#cascadeCopyOut JObject.cascadeCopyOut()}, or * {@link org.jsimpledb.JObject#cascadeCopyTo 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. * * @return whether the field is indexed * @see org.jsimpledb.JObject#cascadeCopyTo JObject.cascadeCopyTo() * @see org.jsimpledb.JTransaction#cascadeFindAll JTransaction.cascadeFindAll() */ String[] cascades() default {}; /** * Define inverse copy cascades for the annotated reference field. * *

* When {@link org.jsimpledb.JObject#cascadeCopyIn JObject.cascadeCopyIn()}, * {@link org.jsimpledb.JObject#cascadeCopyOut JObject.cascadeCopyOut()}, or * {@link org.jsimpledb.JObject#cascadeCopyTo 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. * * @return whether the field is indexed * @see org.jsimpledb.JObject#cascadeCopyTo JObject.cascadeCopyTo() * @see org.jsimpledb.JTransaction#cascadeFindAll JTransaction.cascadeFindAll() */ String[] inverseCascades() default {}; /** * For reference fields, configure the behavior when the referred-to object is * {@linkplain org.jsimpledb.JObject#delete deleted}. * *

* For non-reference fields this property must be equal to its default value. * * @return desired behavior when a referenced object is deleted * @see #cascadeDelete * @see org.jsimpledb.JObject#delete */ DeleteAction onDelete() default DeleteAction.EXCEPTION; /** * For reference fields, configure cascading behavior when the referring object is * {@linkplain org.jsimpledb.JObject#delete 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. * * @return whether deletion should cascade to the referred-to object * @see #onDelete * @see org.jsimpledb.JObject#delete */ boolean cascadeDelete() default false; /** * 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 {@code groups() = }{ {@link javax.validation.groups.Default}{@code .class, * }{@link org.jsimpledb.UniquenessConstraints}{@code .class} }. Therefore, uniqueness constraints * are included in default validation, but you can also validate only uniqueness constraints via * {@link org.jsimpledb.JObject#revalidate myobj.revalidate(UniquenessConstraints.class)}. * *

* This property must be false for sub-fields of complex fields, and for any field that is not indexed. * * @return whether the field's value should be unique * @see #uniqueExclude * @see org.jsimpledb.UniquenessConstraints */ boolean unique() default false; /** * Specify field value(s) which are excluded from the uniqueness constraint. * *

* The specified values must be valid {@link String} encodings of the associated field (as returned by * {@link org.jsimpledb.core.FieldType#toString(Object) FieldType.toString()}), the constant {@link #NULL} * to indicate null values. 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 {@link #unique} is false. * * @return values to exclude from the uniqueness constraint * @see #unique */ String[] uniqueExclude() default {}; /** * 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 {@link org.jsimpledb.core.DeletedObjectException} will be thrown. When used together with * {@link DeleteAction#EXCEPTION} (see {@link #onDelete}), the field is guaranteed to never be a dangling reference. * *

* This property only controls validation in regular (non-snapshot transactions); {@link #allowDeletedSnapshot} * separately controls validation for {@link org.jsimpledb.SnapshotJTransaction}s. * *

* For consistency, this property must be set to true when {@link #onDelete} is set to {@link DeleteAction#NOTHING}. * * @return whether the reference field should allow assignment to deleted objects in normal transactions * @see #onDelete * @see #allowDeletedSnapshot * @see JSimpleClass#autogenAllowDeleted */ boolean allowDeleted() default false; /** * Allow the field to reference non-existent objects in snapshot transactions. * *

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

* This property is equivalent to {@link #allowDeleted}, but applies to {@link org.jsimpledb.SnapshotJTransaction}s * instead of normal {@link org.jsimpledb.JTransaction}s; see {@link #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 {@link #onDelete} is set to {@link DeleteAction#NOTHING}. * * @return whether the reference field should allow assignment to deleted objects in snapshot transactions * @see #onDelete * @see #allowDeleted * @see JSimpleClass#autogenAllowDeletedSnapshot */ boolean allowDeletedSnapshot() default true; /** * Specify the {@link UpgradeConversionPolicy} policy to apply when a schema change occurs and this field's type changes. * *

* With one restriction*, JSimpleDB 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 {@code int} value {@code 1234} * to the {@link String} value {@code "1234"}). * *

* See {@link org.jsimpledb.core.FieldType#convert} for details about conversions between simple field types. In addition, * {@link org.jsimpledb.Counter} fields can be converted to/from any numeric Java primitive (or primitive wrapper) type. * *

* This property defines the {@link 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 * {@link OnVersionChange @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. * * @return upgrade conversion policy for this field * @see UpgradeConversionPolicy * @see org.jsimpledb.core.FieldType#convert FieldType.convert() */ UpgradeConversionPolicy upgradeConversion() default UpgradeConversionPolicy.ATTEMPT; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy