Skip to content

Persistence Adapter

Jan Wiemer edited this page Dec 30, 2020 · 8 revisions

Persistence Adapter

The default version of the JACIS does not focus on the durability (the D from the ACID transaction properties. However it is possible to extend the store with a durable persistent storage of the committed objects. For this purpose the JACIS provides an extension point where different persistence frameworks can be attached to the store.

The Persistence Adapter Extension Point

An extension enabling the persistent storage of the objects is called persistence adapter and has to implement the JacisPersistenceAdapter interface. The persistence adapter can be set at the JacisObjectTypeSpec with method setPersistenceAdapter. By default there is no persistence adapter set at the object type specification and nothing is persisted at all.

The following methods have to be implemented to fulfill the JacisPersistenceAdapter interface:

void initializeStore(JacisStoreImpl<K, V, ?> store);

Called after the creation of the store to initialize the objects in the store from the persistent storage. The objects could be stored using the normal API in the store, but it is also possible to use the initStoreNonTransactional methods.

void onModification(K key, V oldValue, V newValue, JacisTransactionHandle tx);

Inherited from the JacisModificationListener interface. The method is called for each object cloned back from a TX-View to the committed objects (see chapter Modification Listener).

void prepareCommit();

Called after the prepare phase for the store has been executed.

void commit();

Called after the commit for the store has been executed.

void rollback();

Called after a rollback for the store has been executed.

The Microstream Persistence Adapter

One implementation of the persistence adapter is already shipped with the JACIS project, the MicrostreamPersistenceAdapter. This extension uses the MicroStream framework to serialize the committed objects to the file system (or a different persistent storage). The advantage of the MicroStream serialization is that it serializes the Java objects (stored in the JACIS) as they are. There is no need to specify any metadata for any mapping to a persistent data structure (like object-relational mapping required e.g. for the Java Persistence API (JPA)). Another important feature, distinguishing it from the normal Java serialization, is that after modifications not the whole object graph, but only the changed objects have to be written again. More details regarding the features of the MicroStream framework con be found in the MicroStream User Manual.

To use the MicroStream persistence adapter we first have to include the needed dependencies to our application (here in gradle format):

dependencies {
    implementation group: 'org.jacis', name: 'jacis', version: '2.0.0-SNAPSHOT'
    implementation group: 'one.microstream', name: 'storage.embedded', version: '04.00.00-MS-GA'
    implementation group: 'one.microstream', name: 'storage.embedded.configuration', version: '04.00.00-MS-GA'
}

With this the following example shows how to use the persistence adapter:

  public static void main(String[] args) {
    { // first start a container and a store with persistence
      JacisContainer container = new JacisContainer();
      JacisObjectTypeSpec<String, Account, Account> objectTypeSpec //
          = new JacisObjectTypeSpec<>(String.class, Account.class, new JacisCloningObjectAdapter<>());
      // start a MicroStream storage manager
      EmbeddedStorageManager storageManager = createMicroStreamStorageManager();
      // setBase.createEmbeddedStorageManager();
      // set the persistence adapter extension using the storage manager
      objectTypeSpec.setPersistenceAdapter(new MicrostreamPersistenceAdapter<>(storageManager));
      JacisStore<String, Account> store = container.createStore(objectTypeSpec).getStore();
      // create some objects
      container.withLocalTx(() -> {
        store.update("account1", new Account("account1").deposit(-100));
        store.update("account2", new Account("account2").deposit(10));
        store.update("account3", new Account("account3").deposit(100));
      });
      storageManager.close();
    }
    { // simulate restart and start a new container and a new store
      JacisContainer container = new JacisContainer();
      JacisObjectTypeSpec<String, Account, Account> objectTypeSpec //
          = new JacisObjectTypeSpec<>(String.class, Account.class, new JacisCloningObjectAdapter<>());
      // start a MicroStream storage manager
      EmbeddedStorageManager storageManager = createMicroStreamStorageManager();
      // set the persistence adapter extension using the storage manager
      objectTypeSpec.setPersistenceAdapter(new MicrostreamPersistenceAdapter<>(storageManager));
      JacisStore<String, Account> store = container.createStore(objectTypeSpec).getStore();
      // check the objects are still in the store
      container.withLocalTx(() -> {
        store.stream().forEach(acc -> System.out.println("balance(" + acc.getName() + ")= " + acc.getBalance()));
      });
      storageManager.close();
    }
    System.exit(1);
  }

  private static EmbeddedStorageManager createMicroStreamStorageManager() {
    EmbeddedStorageManager storageManager = Configuration.Default() //
        .setBaseDirectory("var/data-dir")
        .setBackupDirectory("var/backup-dir")
        .createEmbeddedStorageFoundation() //
        .createEmbeddedStorageManager();
    return storageManager;
  }

Next Chapter: Transaction Listener

Clone this wiki locally