JVM | Platform | Status |
---|---|---|
OpenJDK (Temurin) Current | Linux | |
OpenJDK (Temurin) LTS | Linux | |
OpenJDK (Temurin) Current | Windows | |
OpenJDK (Temurin) LTS | Windows |
The darco
package provides a minimalist, opinionated API for database access.
- Versioned schema upgrades provided by trasco.
- Pluggable query support.
- Instrumented with OpenTelemetry.
- PostgreSQL support.
- SQLite support.
- Written in pure Java 17.
- OSGi ready
- JPMS ready
- ISC license
- High-coverage automated test suite
Many io7m packages talk to various databases. For example:
- idstore uses PostgreSQL as the underlying persistent data store.
- cardant uses PostgreSQL as the underlying persistent data store.
- northpike uses PostgreSQL as the underlying persistent data store.
- certusine uses SQLite as the underlying persistent data store.
- looseleaf uses SQLite as the underlying persistent data store.
- ...
All of the listed projects abstracted the database behind a simplified set of interfaces to allow for easier unit testing, and to allow for migrating to different databases without having to completely rewrite all of the application code. This meant that each one of those projects had to implement its own set of nearly-identical interfaces and initialization code boilerplate.
In much the same manner as the
hibiscus API attempts to
provide a common interface around RPC clients, and the
anethum API attempts to
provide a common interface around parsers and serializers,
the darco
package attempts to provide a common set of interfaces and abstract
classes to minimize the amount of essentially duplicated code between projects
that talk to databases.
$ mvn clean verify
The com.io7m.darco.api
package provides the basic interfaces that abstract
over a relational database. Projects are expected to extend various provided
abstract classes, and implement various interfaces to provide their own
database abstractions. The package exposes a database as small set of core
types listed in the following sections.
DDatabaseFactoryType databases;
DDatabaseConfigurationType configuration;
try (DDatabaseType database = databases.open(configuration, () -> {})) {
try (DDatabaseConnectionType connection = database.openConnection()) {
try (DDatabaseTransactionType transaction = connection.openTransaction()) {
DDatabaseQueryType<String, DDatabaseUnit> query =
transaction.query(SomeCustomQueryType.class);
query.execute("Hello!");
query.execute("Goodbye!");
transaction.commit();
}
}
}
The DDatabaseFactoryType
interface represents objects that provide
database instances. When a DDatabaseFactoryType
is provided with a
DDatabaseConfigurationType value, it
yields a DDatabaseType instance.
The DDatabaseConfigurationType
interface represents the basic configuration
properties required to open and/or connect to a database.
The DDatabaseType
interface represents an open database. To interact with
the database, callers must call the openConnection()
method to obtain a
DDatabaseConnectionType
instance. In a typical request/response server
application, the application would obtain a new connection for each incoming
client request, and close()
the connection after servicing the request.
The DDatabaseConnectionType
interface represents an open connection to
a database. In order to perform database queries using the connection,
applications must create transactions within
which to execute queries by calling the openTransaction()
method.
The DDatabaseTransactionType
interface represents a database transaction.
Applications perform work in transactions by requesting instances of
queries from the transaction. Transactions must be
explicitly committed, otherwise the work performed by queries inside the
transaction is implicitly rolled back.
The DDatabaseQueryType
interface represents a single query that can be
executed within a transaction. A query typically abstracts over one or more
SQL statements. Queries are strongly typed and are represented as functions
taking parameters of type P
and returning results of type R
.
Query implementations are provided by instances of the
DDatabaseQueryProviderType
interface. Typically, a set of
DDatabaseQueryProviderType
instances are registered using ServiceLoader
.
Applications wishing to provide an abstraction over the
SQLite database should extend the abstract classes
exposed by the com.io7m.darco.sqlite
module.
Applications wishing to provide an abstraction over the
PostgreSQL database should extend the abstract
classes exposed by the com.io7m.darco.postgres
module.
The com.io7m.darco.examples
module provides example implementations of
databases.