Persisting Corda States in Custom Database Tables using QueryableState

January 02, 2020

While the development of Corda, one of our major consideration was to make it easy to use for developers. Thus we used proven technologies to develop the platform rather than coming up with new tools and protocols. Any JVM compatible language (Java, Kotlin, etc.) can be used to develop CorDapps, Artemis was used for messaging between nodes and RDBMS (Relational Database Management System) for storage.

This article will focus on custom schema development in Corda. Corda offers developers the flexibility to persist some or all fields of their contract state to a relational database table via an ORM (Object Relational Mapping) tool. This allows easy integration with organizations existing systems. The Corda state should implement QueryableState to be able to persist information in custom schema.

QueryableState

Every ContractState which is required to be persisted in a custom database table must implement the QueryableState interface. QueryableState defines below two methods which must be defined by the class implementing the interface.

  1. supportedSchema()
  2. generateMappedObject()

The QueryableState interface mandates the state to provide a list of schema it would support. The supported schemas are returned as a list of MappedSchema objects from the supportedSchema() method. Corda nodes run a service calledSchemaService which is responsible for generating the PersistentState object corresponding to the QueryableState by calling the generateMappedObject() method. The PersistentState is then handed to the ORM tool for persisting the state in the database tables.

Steps to implement QueryableState

Step1: Create the QueryableState

QueryableState is defined as a regular ContractState in Corda. It implements the QueryableState interface instead of the ContractState interface. Note that we need to provide the implementation of the two methods supportedSchema() and generateMappedObject(). Let’s leave them blank, for now, I will come back to them later.

Step2: Create the JPA Entity corresponding to the state.

Create the JPA entity corresponding to the state with all the fields required to be persisted in the database table. You could choose to persist all the fields or a subset of them. In this case, I have omitted the insurer and the insuree. The ORM tool would use this entity to store the data on to the custom database table whenever a new state is issued.

Few points to note:

  1. Note that the class extends PersistentState. This ensures that the transaction_id and the output_index of the state appears in the database table.
  2. We haven’t provided any @Id attribute as transaction_id and output_index would be used as a composite primary key in the custom database table.
  3. We need to provide a default constructor since it is required by Hibernate.

Step3: Create a Schema Family

MappedSchema should be associated with a schema family which is consistent across versions of the schema. It allows the SchemaService to select the appropriate version of the schema if it has evolved over time.

public class InsuranceSchemaFamily { }

Step4: Create the MappedSchema

Create a MappedSchema subclass which represents the schema that would be supported by the QueryableState. Provide the schema family, the version and the list of all PersistentState (JPA entity) corresponding to the QueryableState.

Step5: Implement the supportedSchema() method in the QueryableState

As mentioned above the supportedSchema() returns a list of MappedSchema objects supported by the QueryableState. In this case, we need to return an instance of InsuranceSchemaV1 which is a subclass of MappedSchema.

@NotNull
@Override
public Iterable<MappedSchema> supportedSchemas() {
    return ImmutableList.of(new InsuranceSchemaV1());
}

Step6: Implement the generateMappedObject() method in the QueryableState

The generateMappedObject() is where we define the logic to generate the persistent representation of the state. The persistent representation is our JPA entity we defined earlier.

One we have all the above setup in place and the cordapp jar is deployed on the Corda node, the database table for the Insurance state will be created on node startup. All Insurance state created in the vault would also be stored in the database tables.

Here is a snapshot of the database table created after the state is issued onto the ledger

INSURANCE_DETAILS

Since I am concentrating on QueryableStates in this blog, I am not going into the details of how to create a flow to issue the state onto the ledger. If you need to understand how to implement the flows, you can find the link to the Sample Code below.

Sample Code

The sample code used in this blog can be found here:

corda/samples

Further reading

More information on QueryableState can be found on the Corda Docs site. Extra detail not included in this post can be found there.

The second part of this blog can be found here. It concentrates on persisting hierarchical relationship using queryable state.

If you have come this far it means you are genuinely interested in Corda. You may consider joining us in our public slack channel if you have questions or are interested in learning more about Corda. Other ways to connect.

— Ashutosh Meher, Developer Relations at R3


Persisting Corda States in Custom Database Tables using QueryableState was originally published in Corda on Medium, where people are continuing the conversation by highlighting and responding to this story.