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 value fields containing any standard or custom type that has an Encoding
  • Invertable reference fields with referential integrity and configurable delete cascading
  • Lockless counter fields
  • Complex fields of type List, NavigableSet, and NavigableMap
  • 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 migration with 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.
  • Simple fields are encoded/decoded into byte[] arrays using an Encoding which defines the field's "type".
  • References from one object to another are represented by ObjIds and encoded/decoded using ReferenceEncoding.
  • There is no explicit notion of object sub-types, i.e., the object type hierarchy is completely flat. However, object types can share the same field definitions, and reference fields can be restricted to only refer to certain other object types.
  • There is no automatic validation support.

Schema Tracking

Within each Database is stored a record of all schemas used with that database (termed the "schema bundle"). When creating a new transaction, the caller provides a SchemaModel to use for accessing objects in the transaction. If the schema is not already registered in the database, it is automatically registered if at the start of the transaction assuming TransactionConfig.isAllowNewSchema() is true (otherwise a SchemaMismatchException is thrown).

Transactions can be opened with an empty SchemaModel when no particular schema is needed. In any case, the empty SchemaModel is never recorded in a database.

Schema Migration

Each object in a Database is associated with an object type in one of the registered schemas; this dictates which fields the object contains, how they are indexed, etc. Newly created objects are always defined in terms of the transaction's configured schema, but it's also possible to access objects that were created in previous transactions under different schemas.

When an object is accessed during a Transaction, if the object's schema is not the transaction's configured schema, then (if requested) the object will be automatically migrated to the transaction's schema if the object's type still exists (otherwise a TypeNotInSchemaException is thrown).

Object migration involves the following steps:

  • Fields that exist in the old schema but not in the current schema are removed.
  • Fields that exist in the current schema but not in the old schema are initialized to their default values.
  • For fields that are common to both the old and the new schema:
    • If the two field's Encodings are the same, the value is not changed
    • Otherwise, the field's old value is converted to the new Encoding, if possible, via Encoding.convert().
    • Otherwise, the field is reset to its default value.
  • Any SchemaChangeListeners registered with the Transaction are notified.

Object type and field identity is based on names. So when comparing across schemas, two object types or fields are assumed to be "the same" if they have the same name.

Indexes may be added and removed across different schemas without losing information, however, indexes will only contain those objects whose schema includes the index. In other words, it's not possible to find an object using an index that was added after the object was created until the object is migrated to a newer schema. The same principle applies to other schema-defined behavior, for example, the DeleteAction taken when a referenced object is deleted depends on the DeleteAction configured in the schema of the object containing the reference.

See Also: