Skip to content

Commit a311115

Browse files
authored
Merge pull request #239 from ammachado/hsql-derby
Support for HSQLDB and Apache Derby
2 parents 228374c + 6b659f1 commit a311115

20 files changed

+727
-25
lines changed

README.md

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The primary goal of this project is to make it easier to write Spring-powered in
1010
* Spring `4.3.8` - `6.0.x`
1111
* Spring Boot `1.4.6` - `3.0.x`
1212
* Supports multiple different databases
13-
* [PostgreSQL](#postgresql), [MSSQL](#microsoft-sql-server), [MySQL](#mysql), [MariaDB](#mariadb), [H2](#h2)
13+
* [PostgreSQL](#postgresql), [MSSQL](#microsoft-sql-server), [MySQL](#mysql), [MariaDB](#mariadb), [H2](#h2), [HSQLDB](#hsqldb) and [Derby](#derby)
1414
* Supports multiple database providers
1515
* [Docker / Testcontainers](#using-docker-provider-default), [Zonky](#using-zonky-provider-previous-default), [OpenTable](#using-opentable-provider), [Yandex](#using-yandex-provider)
1616
* Supports various database migration tools
@@ -348,6 +348,46 @@ Before you use H2 database, you have to add the following Maven dependency:
348348
**Note that the associated database provider supports database prefetching, but not template databases.
349349
So you may notice some performance degradation compared to other database providers in some cases.**
350350

351+
### HSQLDB
352+
353+
This provider may be convenient if you have an application that supports multiple databases
354+
and you want to reuse the tests using the `@AutoConfigureEmbeddedDatabase` annotation for all these databases, including the HSQLDB database.
355+
In this case, you can override the `zonky.test.database.provider` property externally and change the used database provider for each run without changing the code.
356+
You can find more information about externalized configuration here: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
357+
358+
Before you use HSQLDB database, you have to add the following Maven dependency:
359+
360+
```xml
361+
<dependency>
362+
<groupId>org.hsqldb</groupId>
363+
<artifactId>hsqldb</artifactId>
364+
<version>2.7.1</version>
365+
</dependency>
366+
```
367+
368+
**Note that the associated database provider supports database prefetching, but not template databases.
369+
So you may notice some performance degradation compared to other database providers in some cases.**
370+
371+
### Derby
372+
373+
This provider may be convenient if you have an application that supports multiple databases
374+
and you want to reuse the tests using the `@AutoConfigureEmbeddedDatabase` annotation for all these databases, including the Apache Derby database.
375+
In this case, you can override the `zonky.test.database.provider` property externally and change the used database provider for each run without changing the code.
376+
You can find more information about externalized configuration here: https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
377+
378+
Before you use Apache Derby database, you have to add the following Maven dependency:
379+
380+
```xml
381+
<dependency>
382+
<groupId>org.apache.derby</groupId>
383+
<artifactId>derby</artifactId>
384+
<version>10.16.1.1</version>
385+
</dependency>
386+
```
387+
388+
**Note that the associated database provider supports database prefetching, but not template databases.
389+
So you may notice some performance degradation compared to other database providers in some cases.**
390+
351391
## Supported Migration Tools
352392

353393
Note that although any migration tool is supported,

build.gradle

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ ext {
1212

1313
testSuites = [
1414
[name: 'default', versions: [
15-
[name: 'suite', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default']
15+
[name: 'suite', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default']
1616
]],
1717
[name: 'spring', versions: [
18-
[name: '4.3.8', spring: '4.3.8.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default'],
19-
[name: '4.3.30', spring: '4.3.30.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default'],
20-
[name: '5.0.20', spring: '5.0.20.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default'],
21-
[name: '5.1.20', spring: '5.1.20.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default'],
22-
[name: '5.2.22', spring: '5.2.22.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default'],
23-
[name: '5.3.23', spring: '5.3.23', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default']
18+
[name: '4.3.8', spring: '4.3.8.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default'],
19+
[name: '4.3.30', spring: '4.3.30.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default'],
20+
[name: '5.0.20', spring: '5.0.20.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default'],
21+
[name: '5.1.20', spring: '5.1.20.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default'],
22+
[name: '5.2.22', spring: '5.2.22.RELEASE', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default'],
23+
[name: '5.3.23', spring: '5.3.23', 'zonky-postgres': 'default', opentable: 'default', yandex: 'default', 'mssql-driver': 'default', 'mysql-driver': 'default', 'mariadb-driver': 'default', 'h2': 'default', 'hsqldb': 'default', 'derby': 'default']
2424
]],
2525
[name: 'flyway', versions: [
2626
[name: '4.0.3', flyway: '4.0.3', 'flyway-test': '4.0.1', spring: '4.3.30.RELEASE', 'spring-boot': '1.5.22.RELEASE', 'zonky-postgres': 'default'],
@@ -77,10 +77,29 @@ ext {
7777
[name: '1.4.200', 'h2': '1.4.200'],
7878
[name: '2.0.206', 'h2': '2.0.206'],
7979
[name: '2.1.214', 'h2': '2.1.214']
80+
]],
81+
[name: 'hsqldb', versions: [
82+
[name: '2.3.6', 'hsqldb': '2.3.6'],
83+
[name: '2.4.1', 'hsqldb': '2.4.1'],
84+
[name: '2.5.2', 'hsqldb': '2.5.2']
85+
]],
86+
[name: 'derby', versions: [
87+
[name: '10.13.1.1', 'derby': '10.13.1.1'],
88+
[name: '10.14.2.0', 'derby': '10.14.2.0']
8089
]]
8190
]
8291
}
8392

93+
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) {
94+
testSuites.find { it.name == 'hsqldb' }.versions << [name: '2.7.1', 'hsqldb': '2.7.1']
95+
}
96+
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_1_9)) {
97+
testSuites.find { it.name == 'derby' }.versions << [name: '10.15.2.0', 'derby': '10.15.2.0']
98+
}
99+
if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_17)) {
100+
testSuites.find { it.name == 'derby' }.versions << [name: '10.16.1.1', 'derby': '10.16.1.1']
101+
}
102+
84103
allprojects {
85104
group 'io.zonky.test'
86105
}
@@ -107,12 +126,12 @@ subprojects {
107126
}
108127

109128
task sourcesJar(type: Jar) {
110-
classifier = 'sources'
129+
archiveClassifier = 'sources'
111130
from sourceSets.main.allSource
112131
}
113132

114133
task javadocJar(type: Jar) {
115-
classifier = 'javadoc'
134+
archiveClassifier = 'javadoc'
116135
from javadoc
117136
}
118137

@@ -213,6 +232,9 @@ project(':embedded-database-spring-test') {
213232
compile 'mysql:mysql-connector-java:8.0.30', optional
214233
compile 'org.mariadb.jdbc:mariadb-java-client:3.1.0', optional
215234
compile 'com.h2database:h2:2.1.214', optional
235+
compile 'org.hsqldb:hsqldb:2.5.2', optional
236+
compile 'org.apache.derby:derby:10.14.2.0', optional
237+
compile 'org.apache.derby:derbytools:10.14.2.0', optional
216238

217239
compile 'org.flywaydb:flyway-core:9.8.2', optional
218240
compile 'org.flywaydb.flyway-test-extensions:flyway-spring-test:7.0.0', optional
@@ -370,6 +392,26 @@ project(':embedded-database-spring-test') {
370392
}
371393
}
372394
}
395+
396+
if (version.hsqldb == null) { // optional dependencies are implicitly excluded
397+
exclude group: 'org.hsqldb', module: 'hsqldb'
398+
} else if (version.hsqldb != 'default') {
399+
eachDependency { details ->
400+
if (details.requested.group == 'org.hsqldb' && details.requested.name == 'hsqldb') {
401+
details.useVersion "${version.hsqldb}"
402+
}
403+
}
404+
}
405+
406+
if (version.derby == null) { // optional dependencies are implicitly excluded
407+
exclude group: 'org.apache.derby'
408+
} else if (version.derby != 'default') {
409+
eachDependency { details ->
410+
if (details.requested.group == 'org.apache.derby') {
411+
details.useVersion "${version.derby}"
412+
}
413+
}
414+
}
373415
}
374416
}
375417
}
@@ -414,6 +456,8 @@ project(':embedded-database-spring-test') {
414456
excludeCategories 'io.zonky.test.category.MySQLTestSuite'
415457
excludeCategories 'io.zonky.test.category.MariaDBTestSuite'
416458
excludeCategories 'io.zonky.test.category.H2TestSuite'
459+
excludeCategories 'io.zonky.test.category.HSQLTestSuite'
460+
excludeCategories 'io.zonky.test.category.DerbyTestSuite'
417461
}
418462
}
419463

@@ -456,6 +500,10 @@ project(':embedded-database-spring-test') {
456500
includeCategories 'io.zonky.test.category.MariaDBTestSuite'
457501
} else if (suite.name == 'h2') {
458502
includeCategories 'io.zonky.test.category.H2TestSuite'
503+
} else if (suite.name == 'hsqldb') {
504+
includeCategories 'io.zonky.test.category.HSQLTestSuite'
505+
} else if (suite.name == 'derby') {
506+
includeCategories 'io.zonky.test.category.DerbyTestSuite'
459507
}
460508
}
461509
}

embedded-database-spring-test/src/main/java/io/zonky/test/db/AutoConfigureEmbeddedDatabase.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
/**
6666
* The type of embedded database to be created when {@link #replace() replacing} the data source.
67-
* By default will attempt to detect the database type based on the classpath.
67+
* By default, will attempt to detect the database type based on the classpath.
6868
*
6969
* <p>The database type may also be configured via the {@code zonky.test.database.type} configuration property.
7070
*
@@ -155,7 +155,17 @@ enum DatabaseType {
155155
/**
156156
* H2 Database
157157
*/
158-
H2
158+
H2,
159+
160+
/**
161+
* HSQL Database
162+
*/
163+
HSQL,
164+
165+
/**
166+
* Apache Derby
167+
*/
168+
DERBY
159169

160170
}
161171

@@ -206,7 +216,7 @@ enum DatabaseProvider {
206216
enum Replace {
207217

208218
/**
209-
* Replace any DataSource bean (auto-configured or manually defined).
219+
* Replace any DataSource bean (autoconfigured or manually defined).
210220
*/
211221
ANY,
212222

embedded-database-spring-test/src/main/java/io/zonky/test/db/config/EmbeddedDatabaseAutoConfiguration.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import io.zonky.test.db.liquibase.LiquibaseDatabaseExtension;
2222
import io.zonky.test.db.liquibase.LiquibasePropertiesPostProcessor;
2323
import io.zonky.test.db.provider.DatabaseProvider;
24+
import io.zonky.test.db.provider.derby.DerbyDatabaseProvider;
2425
import io.zonky.test.db.provider.h2.H2DatabaseProvider;
26+
import io.zonky.test.db.provider.hsqldb.HSQLDatabaseProvider;
2527
import io.zonky.test.db.provider.mariadb.DockerMariaDBDatabaseProvider;
2628
import io.zonky.test.db.provider.mssql.DockerMSSQLDatabaseProvider;
2729
import io.zonky.test.db.provider.mysql.DockerMySQLDatabaseProvider;
@@ -123,6 +125,26 @@ public DatabaseProvider h2DatabaseProvider(DatabaseProviderFactory h2DatabasePro
123125
return h2DatabaseProviderFactory.createProvider(H2DatabaseProvider.class);
124126
}
125127

128+
@Bean
129+
@Provider(type = "embedded", database = "hsql")
130+
@ConditionalOnMissingBean(name = "hsqlDatabaseProvider")
131+
public DatabaseProvider hsqlDatabaseProvider(DatabaseProviderFactory hsqlDatabaseProviderFactory) {
132+
checkDependency("org.hsqldb", "hsqldb", "org.hsqldb.jdbcDriver");
133+
return hsqlDatabaseProviderFactory.createProvider(HSQLDatabaseProvider.class);
134+
}
135+
136+
@Bean
137+
@Provider(type = "embedded", database = "derby")
138+
@ConditionalOnMissingBean(name = "derbyDatabaseProvider")
139+
public DatabaseProvider derbyDatabaseProvider(DatabaseProviderFactory derbyDatabaseProviderFactory) {
140+
if (!ClassUtils.isPresent("org.apache.derby.info.engine.DerbyModule", classLoader)) {
141+
checkDependency("org.apache.derby", "derby", "org.apache.derby.jdbc.EmbeddedDriver");
142+
} else {
143+
checkDependency("org.apache.derby", "derbytools", "org.apache.derby.jdbc.EmbeddedDriver");
144+
}
145+
return derbyDatabaseProviderFactory.createProvider(DerbyDatabaseProvider.class);
146+
}
147+
126148
@Bean
127149
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
128150
@ConditionalOnMissingBean(name = "postgresDatabaseProviderFactory")
@@ -168,6 +190,24 @@ public DatabaseProviderFactory h2DatabaseProviderFactory(DatabaseProviderFactory
168190
builder.prefetchingProvider(provider)));
169191
}
170192

193+
@Bean
194+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
195+
@ConditionalOnMissingBean(name = "hsqlDatabaseProviderFactory")
196+
public DatabaseProviderFactory hsqlDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
197+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
198+
builder.optimizingProvider(
199+
builder.prefetchingProvider(provider)));
200+
}
201+
202+
@Bean
203+
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
204+
@ConditionalOnMissingBean(name = "derbyDatabaseProviderFactory")
205+
public DatabaseProviderFactory derbyDatabaseProviderFactory(DatabaseProviderFactory defaultDatabaseProviderFactory) {
206+
return defaultDatabaseProviderFactory.customizeProvider((builder, provider) ->
207+
builder.optimizingProvider(
208+
builder.prefetchingProvider(provider)));
209+
}
210+
171211
@Bean
172212
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
173213
@ConditionalOnMissingBean(name = "defaultDatabaseProviderFactory")
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.zonky.test.db.provider.derby;
18+
19+
import io.zonky.test.db.preparer.DatabasePreparer;
20+
import io.zonky.test.db.provider.DatabaseProvider;
21+
import io.zonky.test.db.provider.EmbeddedDatabase;
22+
import io.zonky.test.db.provider.ProviderException;
23+
import org.apache.derby.jdbc.EmbeddedDriver ;
24+
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
25+
26+
import java.sql.SQLException;
27+
import java.util.Objects;
28+
import java.util.Properties;
29+
import java.util.UUID;
30+
import java.util.concurrent.CompletableFuture;
31+
32+
public class DerbyDatabaseProvider implements DatabaseProvider {
33+
34+
private static final String URL_TEMPLATE = "jdbc:derby:memory:%s;%s";
35+
36+
@Override
37+
public EmbeddedDatabase createDatabase(DatabasePreparer preparer) throws ProviderException {
38+
SimpleDriverDataSource dataSource = new SimpleDriverDataSource();
39+
String databaseName = UUID.randomUUID().toString();
40+
41+
dataSource.setDriverClass(EmbeddedDriver.class);
42+
dataSource.setUrl(String.format(URL_TEMPLATE, databaseName, "create=true"));
43+
dataSource.setUsername("sa");
44+
dataSource.setPassword("");
45+
46+
DerbyEmbeddedDatabase database = new DerbyEmbeddedDatabase(dataSource, databaseName,
47+
() -> shutdownDatabase(databaseName));
48+
try {
49+
if (preparer != null) {
50+
preparer.prepare(database);
51+
}
52+
} catch (SQLException e) {
53+
throw new ProviderException("Unexpected error when creating a database", e);
54+
}
55+
return database;
56+
}
57+
58+
@Override
59+
public boolean equals(Object o) {
60+
if (this == o) return true;
61+
return o != null && getClass() == o.getClass();
62+
}
63+
64+
@Override
65+
public int hashCode() {
66+
return Objects.hash(DerbyDatabaseProvider.class);
67+
}
68+
69+
private static void shutdownDatabase(String dbName) {
70+
CompletableFuture.runAsync(() -> {
71+
try {
72+
new EmbeddedDriver().connect(String.format(URL_TEMPLATE, dbName, "drop=true"), new Properties());
73+
} catch (SQLException e) {
74+
// nothing to do
75+
}
76+
});
77+
}
78+
}

0 commit comments

Comments
 (0)