Class Database


  • public class Database
    extends Object
    Provides an object database abstraction on top of a key/value database.

    Includes support for:

    • Objects and fields defined by a SchemaModel, with positive schema verification
    • Simple values fields containing any atomic type, reference or custom FieldType
    • Complex fields of type List, NavigableSet, and NavigableMap
    • Invertable reference fields with strong referential integrity and configurable delete cascading
    • Configurable indexing of any simple field or complex sub-field
    • Composite indexes on multiple simple fields
    • Notification of object creation and deletion
    • Notification of object field changes, as seen through an arbitrary path of references
    • Automatic schema tracking and object versioning with schema change notification support

    See Transaction for further details on the above functionality.

    This class defines an abstraction layer that usually sits below a Permazen but is completely independent of Permazen and can be used on its own. Compared to Permazen, a Database has these differences:

    • A SchemaModel must be explicitly provided to define the schema in use, whereas when using a Permazen the schema is derived automatically from annotated Java model classes.
    • Object references are represented by ObjIds instead of Java objects, and there is no notion of object sub-type. However, reference fields may be configured with a restricted set of referrable types.
    • All object types and fields are referenced by explicit storage ID.
    • Enum values are represented by EnumValue objects.
    • There is no automatic validation support.
    See Also:
    Transaction, io.permazen
    • Field Detail

      • MAX_INDEXED_FIELDS

        public static final int MAX_INDEXED_FIELDS
        The maximum number of fields that may be indexed in a composite index (4).
        See Also:
        Constant Field Values
    • Constructor Detail

      • Database

        public Database​(KVDatabase kvdb)
        Constructor.
        Parameters:
        kvdb - the underlying key/value store in which to store information
        Throws:
        IllegalArgumentException - if kvdb is null
    • Method Detail

      • getFieldTypeRegistry

        public FieldTypeRegistry getFieldTypeRegistry()
        Get the FieldTypeRegistry associated with this instance.
        Returns:
        field type registry associated with this instance
      • getKVDatabase

        public KVDatabase getKVDatabase()
        Get the KVDatabase underlying this instance.
        Returns:
        underlying key/value database
      • createTransaction

        public Transaction createTransaction​(SchemaModel schemaModel,
                                             int version,
                                             boolean allowNewSchema)
        Create a new transaction.

        Convenience method; equivalent to:

          createTransaction(schemaModel, version, allowNewSchema, null);
          
        Parameters:
        schemaModel - schema to use with the new transaction, or null to use the schema already recorded in the database
        version - the schema version number corresponding to schemaModel, zero to use the highest version already recorded in the database, or -1 to use an auto-generated schema version
        allowNewSchema - whether creating a new schema version is allowed
        Returns:
        newly created transaction
        Throws:
        IllegalArgumentException - if version is less than -1, or equal to -1 when schemaModel is null
        InvalidSchemaException - if schemaModel is invalid (i.e., does not pass validation checks)
        SchemaMismatchException - if schemaModel does not match schema version version as recorded in the database
        SchemaMismatchException - if schema version version is not recorded in the database and allowNewSchema is false
        SchemaMismatchException - if schema version version is not recorded in the database, allowNewSchema is true, but schemaModel is incompatible with one or more other schemas already recorded in the database (i.e., the same storage ID is used inconsistently between schema versions)
        SchemaMismatchException - if the database is uninitialized and version == 0 or schemaModel is null
        InconsistentDatabaseException - if inconsistent or invalid meta-data is detected in the database
        InconsistentDatabaseException - if an uninitialized database is encountered but the database is not empty
        IllegalStateException - if no underlying KVDatabase has been configured for this instance
      • createTransaction

        public Transaction createTransaction​(SchemaModel schemaModel,
                                             int version,
                                             boolean allowNewSchema,
                                             Map<String,​?> kvoptions)
        Create a new Transaction on this database and use the specified schema version to access objects and fields.

        Schema Versions

        Within each Database is stored a record of all schema versions previously used with the database. When creating a new transaction, the caller provides an expected schema version and corresponding SchemaModel. Both of these are optional: a schema version of zero means "use the highest version recorded in the database", and a null SchemaModel means "use the SchemaModel already recorded in the database under version".

        When this method is invoked, the following checks are applied:

        • If a schema with version number version != 0 is recorded in the database, and schemaModel is null or matches it, then this method succeeds, and the Transaction will use that schema.
        • If a schema with version number version (or the highest numbered schema if version == 0) is recorded in the database, and schemaModel is not null and does not match it, then this method fails and throws a SchemaMismatchException.
        • If allowNewSchema is false, and no schema with version number version != 0 has yet been recorded in the database, then this method fails and throws a SchemaMismatchException.
        • If allowNewSchema is true, and no schema with version number version != 0 has yet been recorded in the database, then if schemaModel is null a SchemaMismatchException is thrown; otherwise schemaModel is checked for compabitility with the schemas previously recorded in the database; if compatible, this method succeeds, schema is recorded in the database under the new version number version, and the Transaction will use schema version version; otherwise a SchemaMismatchException is thrown.
        • If the database is uninitialized and version == 0 or schemaModel is null, a SchemaMismatchException is thrown.

        For two schemas to "match", they must be identical in all respects, except that object, field, and index names may differ. In the core API, objects and fields are identified by storage ID, not name.

        Schemas must also be compatible with all other schemas previously recorded in the database. Basically this means storage IDs must be used consistently from a structural point of view:

        • Once a storage ID is assigned, it cannot be re-assigned to a different type of item (object or field).
        • Fields must have a consistent type and structural parent (object type or complex field).

        However, object types and fields may be added or removed across schema versions, field indexing may change, and reference field DeleteActions may change.

        Object Versions

        Each object in a Database contains an internal version number that indicates its current schema version; this in turn dictates what fields that object contains.

        When an object is accessed during a Transaction, the object's version is compared to the version associated with that Transaction. If the versions are the same, no version change occurs and fields are accessed normally.

        If the object has a version oldVersion different from version, then depending on which Transaction method is invoked, the object version may be automatically updated to version. This will cause fields to be added or removed, as follows:

        • Fields that are common to both schema versions remain unchanged (necessarily such fields have the same storage ID, type, and structural parent).
        • Fields that exist in oldVersion but not in version are removed.
        • Fields that exist in version but not in oldVersion are initialized to their default values.
        • All VersionChangeListeners registered with the Transaction are notified.

        Note that compatibility between schema versions does not depend on the field name, nor does it depend on whether the field is indexed, or its DeleteAction (for reference fields). A field's index may be added or removed between schema versions without losing information, however, querying a field's index will only return those objects whose schema version corresponds to a schema in which the field is indexed. Similarly, the DeleteAction taken when a referenced object is deleted depends on the DeleteAction configured in the schema version of the object containing the reference.

        Note that an object's current schema version can go up as well as down, may change non-consecutively, and in fact nothing requires schema version numbers to be consecutive.

        Parameters:
        schemaModel - schema to use with the new transaction, or null to use the schema already recorded in the database
        version - the schema version number corresponding to schemaModel, zero to use the highest version already recorded in the database, or -1 to use an auto-generated schema version
        allowNewSchema - whether creating a new schema version is allowed
        kvoptions - optional KVDatabase-specific transaction options; may be null
        Returns:
        newly created transaction
        Throws:
        IllegalArgumentException - if version is less than -1, or equal to -1 when schemaModel is null
        InvalidSchemaException - if schemaModel is invalid (i.e., does not pass validation checks)
        SchemaMismatchException - if schemaModel does not match schema version version as recorded in the database
        SchemaMismatchException - if schema version version is not recorded in the database and allowNewSchema is false
        SchemaMismatchException - if schema version version is not recorded in the database, allowNewSchema is true, but schemaModel is incompatible with one or more other schemas already recorded in the database (i.e., the same storage ID is used inconsistently between schema versions)
        SchemaMismatchException - if the database is uninitialized and version == 0 or schemaModel is null
        InconsistentDatabaseException - if inconsistent or invalid meta-data is detected in the database
        InconsistentDatabaseException - if an uninitialized database is encountered but the database is not empty
        IllegalStateException - if no underlying KVDatabase has been configured for this instance
      • createTransaction

        public Transaction createTransaction​(KVTransaction kvt,
                                             SchemaModel schemaModel,
                                             int version,
                                             boolean allowNewSchema)
        Create a new Transaction on this database using an already-opened KVTransaction and specified schema. The given KVTransaction will be committed or rolled-back along with the returned Transaction.

        See createTransaction(SchemaModel, int, boolean) for details on schema and object versions.

        Parameters:
        kvt - already opened key/value store transaction
        schemaModel - schema to use with the new transaction, or null to use the schema already recorded in the database
        version - the schema version number corresponding to schemaModel, zero to use the highest version already recorded in the database, or -1 to use an auto-generated schema version
        allowNewSchema - whether creating a new schema version is allowed
        Returns:
        newly created transaction
        Throws:
        IllegalArgumentException - if kvt is null
        IllegalArgumentException - if version is less than -1, or equal to -1 when schemaModel is null
        InvalidSchemaException - if schemaModel is invalid (i.e., does not pass validation checks)
        SchemaMismatchException - if schemaModel does not match schema version version as recorded in the database
        SchemaMismatchException - if schema version version is not recorded in the database and allowNewSchema is false
        SchemaMismatchException - if schema version version is not recorded in the database, allowNewSchema is true, but schemaModel is incompatible with one or more other schemas already recorded in the database (i.e., the same storage ID is used inconsistently between schema versions)
        SchemaMismatchException - if the database is uninitialized and version == 0 or schemaModel is null
        InconsistentDatabaseException - if inconsistent or invalid meta-data is detected in the database
        InconsistentDatabaseException - if an uninitialized database is encountered but the database is not empty
        IllegalStateException - if no underlying KVDatabase has been configured for this instance
      • createSnapshotTransaction

        public SnapshotTransaction createSnapshotTransaction​(KVStore kvstore,
                                                             SchemaModel schemaModel,
                                                             int version,
                                                             boolean allowNewSchema)
        Create a "snapshot" transaction based on the provided key/value store.

        The key/value store will be initialized if necessary (i.e., kvstore may be empty), otherwise it will be validated against the schema information associated with this instance.

        The returned SnapshotTransaction does not support commit(), rollback(), or addCallback(), and can be used indefinitely.

        If kvstore is a CloseableKVStore, then it will be close()'d if/when the returned SnapshotTransaction is.

        Parameters:
        kvstore - key/value store, empty or having content compatible with this transaction's Database
        schemaModel - schema to use with the new transaction, or null to use the schema already recorded in the database
        version - the schema version number corresponding to schemaModel, zero to use the highest version already recorded in the database, or -1 to use an auto-generated schema version
        allowNewSchema - whether creating a new schema version in kvstore is allowed
        Returns:
        snapshot transaction based on kvstore
        Throws:
        IllegalArgumentException - if version is less than -1, or equal to -1 when schemaModel is null
        InvalidSchemaException - if schemaModel is invalid (i.e., does not pass validation checks)
        SchemaMismatchException - if schemaModel does not match schema version version as recorded in the database
        SchemaMismatchException - if schema version version is not recorded in the database and allowNewSchema is false
        SchemaMismatchException - if schema version version is not recorded in the database, allowNewSchema is true, but schemaModel is incompatible with one or more other schemas already recorded in the database (i.e., the same storage ID is used inconsistently between schema versions)
        SchemaMismatchException - if the database is uninitialized and version == 0 or schemaModel is null
        SchemaMismatchException - if kvstore contains incompatible or missing schema information
        InconsistentDatabaseException - if inconsistent or invalid meta-data is detected in the database
        InconsistentDatabaseException - if an uninitialized database is encountered but the database is not empty
        IllegalArgumentException - if kvstore is null
        See Also:
        Transaction.createSnapshotTransaction()
      • validateSchema

        public static void validateSchema​(FieldTypeRegistry fieldTypeRegistry,
                                          SchemaModel schemaModel)
        Validate a SchemaModel.

        This method only statically checks the schema; it does not validate the schema against any other existing schema versions that may be previously recorded in a database.

        To validate a schema against a particular database, simply attempt to create a transaction via createTransaction(). To validate that a collection of schemas are mutually consistent independently from any database, use validateSchemas().

        Parameters:
        fieldTypeRegistry - registry of simple field types
        schemaModel - schema to validate
        Throws:
        InvalidSchemaException - if schemaModel is invalid
        IllegalArgumentException - if either parameter is null
      • validateSchemas

        public static void validateSchemas​(FieldTypeRegistry fieldTypeRegistry,
                                           Collection<SchemaModel> schemaModels)
        Check whether a collection of SchemaModels are individually valid and mutually consistent.

        This method verifies each schema via validateSchema(), and also verifies that the schemas are mututally consistent, i.e., that they do not use storage ID's incompatibly.

        Parameters:
        fieldTypeRegistry - registry of simple field types
        schemaModels - schemas to validate (null elements are ignored)
        Throws:
        InvalidSchemaException - if an element in schemaModels is invalid
        InvalidSchemaException - if the schemaModels are not mutally consistent
        IllegalArgumentException - if either parameter is null