-
Notifications
You must be signed in to change notification settings - Fork 0
/
README.in
145 lines (109 loc) · 5.45 KB
/
README.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# darco
The `darco` package provides a minimalist, opinionated API for database access.
### Features
* Versioned schema upgrades provided by [trasco](https://www.io7m.com/software/trasco).
* Pluggable query support.
* Instrumented with [OpenTelemetry](https://www.opentelemetry.io).
* [PostgreSQL](https://www.postgresql.org) support.
* [SQLite](https://www.sqlite.org) support.
* Written in pure Java 17.
* [OSGi](https://www.osgi.org/) ready
* [JPMS](https://en.wikipedia.org/wiki/Java_Platform_Module_System) ready
* ISC license
* High-coverage automated test suite
### Motivation
Many [io7m](https://www.io7m.com) packages talk to various databases. For
example:
* [idstore](https://www.io7m.com/software/idstore) uses PostgreSQL as the
underlying persistent data store.
* [cardant](https://www.io7m.com/software/cardant) uses PostgreSQL as the
underlying persistent data store.
* [northpike](https://www.io7m.com/software/northpike) uses PostgreSQL as the
underlying persistent data store.
* [certusine](https://www.io7m.com/software/certusine) uses SQLite as the
underlying persistent data store.
* [looseleaf](https://www.io7m.com/software/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](https://www.io7m.com/software/hibiscus) API attempts to
provide a common interface around RPC clients, and the
[anethum](https://www.io7m.com/software/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.
### Building
```
$ mvn clean verify
```
### Usage
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();
}
}
}
```
#### DDatabaseFactoryType
The `DDatabaseFactoryType` interface represents objects that provide
database instances. When a `DDatabaseFactoryType` is provided with a
[DDatabaseConfigurationType](#DDatabaseConfigurationType) value, it
yields a [DDatabaseType](#DDatabaseType) instance.
#### DDatabaseConfigurationType
The `DDatabaseConfigurationType` interface represents the basic configuration
properties required to open and/or connect to a database.
#### DDatabaseType
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.
#### DDatabaseConnectionType
The `DDatabaseConnectionType` interface represents an open connection to
a database. In order to perform database queries using the connection,
applications must create [transactions](#DDatabaseTransactionType) within
which to execute queries by calling the `openTransaction()` method.
#### DDatabaseTransactionType
The `DDatabaseTransactionType` interface represents a database transaction.
Applications perform work in transactions by requesting instances of
[queries](#DDatabaseQueryType) from the transaction. Transactions must be
explicitly _committed_, otherwise the work performed by queries inside the
transaction is implicitly _rolled back_.
#### DDatabaseQueryType
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`.
### SQLite
Applications wishing to provide an abstraction over the
[SQLite](https://www.sqlite.org) database should extend the abstract classes
exposed by the `com.io7m.darco.sqlite` module.
### PostgreSQL
Applications wishing to provide an abstraction over the
[PostgreSQL](https://www.postgresql.org) database should extend the abstract
classes exposed by the `com.io7m.darco.postgres` module.
### Examples
The `com.io7m.darco.examples` module provides example implementations of
databases.