Package io.permazen

Class Permazen


@ThreadSafe public class Permazen extends Object
Permazen Java persistence layer.

Permazen is a Java persistence solution built on three layers of abstraction:

  • At the bottom is a simple Key/Value Layer represented by KVDatabase. Transactions are supported at this layer and are accessed via KVTransaction. There are several available KVDatabase implementations, including wrappers for many third party key/value stores.
  • On top of that sits the Core API Layer, which provides a rigorous "object database" abstraction on top of a KVDatabase. It supports a flat hierarchy of "object" types, where an object consists of fields that are either simple (i.e., any Java type that can be (de)serialized to/from a byte[] array), list, set, or map. It also includes tightly controlled schema tracking, simple and composite indexes, and lifecycle and change notifications. It is not Java-specific or explicitly object-oriented: an "object" at this layer is just a structure with defined fields. The core API layer may be accessed through the Database and Transaction classes.
  • The Java Layer is a Java-centric, object-oriented persistence layer for Java applications. It sits on top of the core API layer and provides a fully "Java" view of the underlying data where all data access is through user-supplied Java model classes. All schema definition and listener registrations are inferred from Java annotations. Incremental JSR 303 validation is supported. The Permazen class represents an instance of this top layer database, and PermazenTransaction represents the corresonding transactions.

User-provided Java model classes define database fields by declaring abstract Java bean methods; Permazen generates concrete subclasses of the user-provided abstract model classes at runtime. These runtime classes will implement the Java bean methods as well as the PermazenObject interface. Instances of Java model classes are always associated with a specific PermazenTransaction, and all of their state derives from the underlying key/value KVTransaction.

Java model class instances have a unique ObjId which represents database identity. Permazen guarantees that at most one Java instance will exist for any given PermazenTransaction and ObjId.


New instance creation, index queries, and certain other database-related tasks are initiated through a PermazenTransaction. Normal transactions are created via createTransaction().

Detached Transactions

Detached transactions are in-memory transactions that are completely detached from the database. They are never committed and may persist indefinitely. Their purpose is to hold a database objects in memory where they can be manipulated like normal Java objects, but with the usual functionality available to open transactions added, such as index queries, schema tracking, change notifications, reference cascades, etc. Detached transactions are initially empty, and are often used to hold a copy of some small portion of the database. Because their state is encoded entirely by in-memory key/value pairs, they are easily serialized/deserialized. They are also useful in non-database applications where a Java data structure in which all reference fields are invertable is needed.

See PermazenTransaction.createDetachedTransaction(), PermazenTransaction.getDetachedTransaction(), PermazenObject.copyOut(), and PermazenObject.copyIn().

Branched Transactions

Branched transactions behave like normal transactions, but internally they are constructed from two separate key/value transactions, one that occurs when the branched transaction is opened and one that occurs at commit time. When a branched transaction is opened, the first key/value transaction is opened just long enoughh to create a read-only snapshot of the database and then closed. The branched transaction then operates entirely in memory, based on the snapshot, and it keeps track of all keys read and written. At commit time, a new key/value transaction is opened and a conflict check is performed. If successful, the accumulated writes are flushed out and the transaction completes, otherwise a TransactionConflictException is thrown.

Because they consume no database resources while open, branched transactions can stay open for an arbitrarily long time, yet they still provide the same consistency guarantees as normal transactions. Of course this comes at the cost of having to track reads and writes in memory and performing conflict checks all at once on commit. Howerver they can be useful in certain scenarios. For example, imagine a user is editing some database object in a GUI application, and the user could take several minutes to complete a form. Because only a single object is being edited, the operation is guaranteed to only access a small amount of data. Using a branched transaction means no database resources are held open while the user decides.

In addition, in many applications it's important to detect conflicts, for example, when two users edit the same object where the second user's changes would otherwise overwite the first user's changes. Traditionally this kind of conflict check is done at the application layer, either by using two separate transactions and applying application-level conflict detection (which is tedious and error-prone), or by relying on database object versioning to detect unexpected changes, which is an incomplete solution because it fails to detect changes in other objects that might have been viewed by the user when deciding what to change (e.g., some other object that the edited object refers to). In other words, traditional database object versioning detects write/write conflicts but not read/write conflicts, and only for the object being written back. Branched transactions provide full consistency in a completely automated way by guaranteeing that all information the user sees while editing a form is still valid when the form's changes are committed.

Branched transactions require that the underlying key/value database support KVTransaction.readOnlySnapshot() and require keeping track of every database read and write during the transaction; see BranchedKVTransaction for details and other caveats. They are created via createBranchedTransaction().


Instances of this class must be initialized before use. This involves accessing the underlying database and registering the Java data model's schema. This will happen automatically (if needed) when any method that requires the registered schema is invoked (including creating a transaction), or by explicit invocation of initialize(). See also PermazenConfig.Builder.initializeOnCreation().

See Also: