Introduction
There are often times in KOS, that in the process of writing an application, it will become necessary to persist information across reboots. There are multiple ways to do this within the framework that KOS provides, but for instances where it is necessary to persist a large amount of structured data, it is typical to use a database. KOS allows users to create and store databases using a combination of SQlite and the JDBI library (Java Database Interface). This article will go into depth on how to create and use databases within KOS.
The Database Migrator
To create a database in KOS, one must implement a DatabaseMigrator. The name derives from the fact that the DatabaseMigrator is responsible for, migrating the structure of, and managing the migration between different database schemas. Each schema is an integer that defines a unique database structure. The scope of its responsibilities includes providing the current schema number, defining the retention behavior for older schemas, and establishing the minimum schema version to use for migration.
There are a number of methods tied to these responsibilities. many of which contain default implementations, with only two exceptions. The two methods that must be implemented by the user are getCurrentSchema(), and migrateToSchema(). The former returns an integer representing the current database schema being used, while the latter defines the actual structure of said schema along with any others. Below is an example implementation of a DatabaseMigrator. In this instance the migrator is only tracking a single schema.
Observe that the method takes as arguments a Jdbi instance, and schema number. This Jdbi instance acts as a wrapper for the DataSource instance being used to access to an actual database. It must be assumed that this Jdbi instance is temporary, and thus it should only be used for migration. It is for this reason setJdbi() is included in this example. While setJdbi() method is technically optional, for instances in which users are using Jdbi as the primary means of interacting with the database, it should be implemented, since it is responsible for setting the final Jdbi instance. It is this instance of Jdbi which should be used for future database queries.
While they will not be demonstrated in this article, there is also a version of migrateToSchema() which takes a DataSource instance rather than a Jdbi instance. Likewise, there is a setDataSource() method for setting a final instance of the DataSource similar to setJdbi(). These can be used in instances where Jdbi is not desired. By default, the DataSource version of migrateToSchema() calls the Jdbi version, so provided the DataSource version is overridden, a blank implementation can be provided for the Jdbi version.
Moving on to the actual implementation of migrateToShema(), the method typically takes the form of a switch statement, wherein each case is tied to a schema. The structure of the database is defined here. KOS comes packaged with SQlite for defining said structure. The example above uses standard SQLite syntax and conventions. To learn more about Sqlite, its syntax, and its various conventions, you can visit this webpage: SQlite. It is important to note that it is entirely up to the user to ensure any change in structure between schemas, carries over data from a previous schema. To demonstrate this, please reference the implementation below.
For this reason, the migrateToSchema() method is called sequentially, starting from the highest schema stored on disk less than the current schema plus one, to the current schema as defined by the migrator. So, for instance, if the highest database schema on disk is two, and the current schema defined by the migrator is four, then this method will be called for schemas three and four. Migrating in this way ensures that jumps in database schemas result in consistent outcomes since at any given moment a database is only migrating to its immediate successor.
Initializing and Accessing the DAO
Once the migrator has been defined, we can create an instance of it to use as a DAO (Data Access Object). Once an instance has been declared, the openDatabase() method of the Application class is called to open/create the database with the specified name.
At this point the DAO can be autowired and accessed from anywhere within the context. That being said, it is up to the user to define the methods that they will want to use to access or modify the database within the DAO. For convenience, below are some example methods for saving an object to, fetching, and deleting it from a database. Everything demonstrated here is generic to JDBI and SQLite and only serves as an example of how objects are typically stored in a database.
