Skip to content

Commit 9f2dede

Browse files
committed
Added minimal TestContainer demo
Added minimal TestContainer demo (maven project). This is a partial port of an existing project that captures software package information on Debian-based systems, with the primary focus at the moment with showing how to use TestContainers and FlywayDB scripts to launch and configure a test database. Testing code using database server extensions is simple - we simply start with a docker image that has already installed the required package, e.g., `postgresql-17-pljava`, and then put `CREATE EXTENSION...` in your flyway script. I'm using jOOQ, not JPA, since I think there may be a way to have the same java classes used in both the database and the application if we have the proper jOOQ binding.
1 parent 5ff7590 commit 9f2dede

40 files changed

+8188
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@ replay_pid*
2525

2626
# editor
2727
.idea/
28+
target
29+
demo/model/target
30+
demo/jooq/target

demo/DEMO.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Demonstration
2+
3+
This is a multi-module maven project that demonstrates how to use test containers
4+
when developing and testing your software.
5+
6+
Perhaps most importantly (for this overall repo) it also demonstrates how to do
7+
these tasks while using database server extensions like PL/Java.
8+
9+
The actual change required is extremely modest:
10+
11+
- Use a docker image that has already installed the appropriate PL/Java package, e.g., `postgresql-16-pljava`.
12+
- Edit the flywaydb migration file to it calls `CREATE EXTENSION...` so the server loads the extension.
13+
14+
The extension should still provide any DDR files required for user-defined functions and types.
15+
16+
*Important note: this is incomplete since I am still migrating the source from a different project.*
17+
18+
## Modules
19+
20+
### Model
21+
22+
This maven module contains:
23+
24+
- the model (what's stored in the database)
25+
- the interfaces used for 'persistence' dependency injection
26+
- the interfaces used for 'service' dependency injection
27+
- possibly some configuration and security classes
28+
29+
In a simple project this module will also include the interfaces
30+
used for 'presentation' dependency injection. These interfaces will
31+
be absent from more complex project since there will be applications
32+
that require different presentations, or lack a presentation layer
33+
entirely.
34+
35+
In contrast every application will need at least some business logic.
36+
In simple projects everything can be defined here, in more complex
37+
projects we may want to define additional functionality in dedicated
38+
maven modules.
39+
40+
*Important note: this module is currently empty*
41+
42+
### JOOQ
43+
44+
Work in progress....
45+
46+
My prior versions used `build-helper-maven-plugin` to put the
47+
autogenerated code into a separate location that was then added
48+
to the source list but in this case I decided to use a dedicated
49+
module that's entirely autogenerated and I'll add my own jooq
50+
code later.
51+
52+
However I've decided that's the wrong approach - there's too many
53+
benefits to having the complete implementation here - including
54+
tests. It won't take long to update but I want to get this under
55+
source control...

demo/jooq/pom.xml

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
7+
<parent>
8+
<groupId>com.coyotesong.examples</groupId>
9+
<artifactId>pljava</artifactId>
10+
<version>1.0-SNAPSHOT</version>
11+
</parent>
12+
<groupId>com.coyotesong.examples.pljava</groupId>
13+
<artifactId>jooq</artifactId>
14+
15+
<!--
16+
- This module auto-generates the jOOQ code using a TestContainer instance
17+
- and a set of "Flyway" configuration files. Flyway provides a clean way
18+
- to manage database changes, and this package will generate the low-level
19+
- jOOQ code that accesses the database.
20+
-
21+
- With a small change this module could auto-generate JPA/Hibernate code.
22+
-
23+
- It's also possible to generate the "model" classes from the database
24+
- schema but that's not without some risk.
25+
-
26+
- We do NOT want to go the other way around - to go from our model to
27+
- database schema - since it is much more difficult to manage database
28+
- updates in this case. It might be manageable if the database model isn't
29+
- too complex and/or shared with others but overall we're better off going
30+
- from a managed schema to the glue logic than from java code to a schema.
31+
-
32+
- DATABASE EXTENSIONS (e.g., PL/Java)
33+
-
34+
- The 'flyway' scripts can easily install database extensions. The only
35+
- requirement is that the extension is already downloaded and put in the
36+
- standard location - we can't download extensions via flyway.
37+
-->
38+
39+
<!-- see https://www.jooq.org/doc/3.18/manual/code-generation/codegen-configuration/ -->
40+
<!-- see https://testcontainers.com/guides/working-with-jooq-flyway-using-testcontainers/ -->
41+
<!-- see https://blog.jooq.org/using-testcontainers-to-generate-jooq-code/ -->
42+
43+
<name>Autogenerate jOOQ code</name>
44+
45+
<description>
46+
Autogenerate jOOQ code. We could just as easily autogenerate JPA/hibernate
47+
code, run integration tests, etc.
48+
</description>
49+
50+
<properties>
51+
<jooq.version>3.19.17</jooq.version>
52+
<flyway.version>11.1.1</flyway.version>
53+
<postgresql.jdbc.version>42.6.0</postgresql.jdbc.version>
54+
</properties>
55+
56+
<dependencyManagement>
57+
<dependencies>
58+
<dependency>
59+
<groupId>org.jooq</groupId>
60+
<artifactId>jooq</artifactId>
61+
<version>${jooq.version}</version>
62+
</dependency>
63+
<dependency>
64+
<groupId>org.flywaydb</groupId>
65+
<artifactId>flyway-core</artifactId>
66+
<version>${flyway.version}</version>
67+
</dependency>
68+
<dependency>
69+
<groupId>org.flywaydb</groupId>
70+
<artifactId>flyway-database-postgresql</artifactId>
71+
<version>${flyway.version}</version>
72+
</dependency>
73+
<dependency>
74+
<groupId>org.postgresql</groupId>
75+
<artifactId>postgresql</artifactId>
76+
<version>${postgresql-jdbc.version}</version>
77+
</dependency>
78+
</dependencies>
79+
</dependencyManagement>
80+
81+
<dependencies>
82+
<dependency>
83+
<groupId>org.jooq</groupId>
84+
<artifactId>jooq</artifactId>
85+
</dependency>
86+
<dependency>
87+
<groupId>org.flywaydb</groupId>
88+
<artifactId>flyway-core</artifactId>
89+
</dependency>
90+
<dependency>
91+
<groupId>org.flywaydb</groupId>
92+
<artifactId>flyway-database-postgresql</artifactId>
93+
</dependency>
94+
<dependency>
95+
<groupId>org.postgresql</groupId>
96+
<artifactId>postgresql</artifactId>
97+
<scope>runtime</scope>
98+
</dependency>
99+
</dependencies>
100+
101+
<build/>
102+
103+
<profiles>
104+
<profile>
105+
<id>codegen</id>
106+
<activation>
107+
<activeByDefault>true</activeByDefault>
108+
</activation>
109+
<properties>
110+
<jooq.version>3.19.17</jooq.version>
111+
<flyway.version>11.1.1</flyway.version>
112+
<groovy-maven-plugin.version>2.1.1</groovy-maven-plugin.version>
113+
<testcontainers.version>1.20.4</testcontainers.version>
114+
<postgresql.jdbc.version>42.6.0</postgresql.jdbc.version>
115+
116+
<jooq.target.packageName>${project.groupId}.persistence.jooq.generated</jooq.target.packageName>
117+
118+
<!-- note: flyway may not support the most recent database releases! -->
119+
<db.container>postgres:16.6-bookworm</db.container>
120+
<db.dbname>test</db.dbname>
121+
<db.username>bob</db.username>
122+
<db.password>password</db.password>
123+
</properties>
124+
<build>
125+
<plugins>
126+
<!-- launch test container -->
127+
<plugin>
128+
<groupId>org.codehaus.gmaven</groupId>
129+
<artifactId>groovy-maven-plugin</artifactId>
130+
<version>2.1.1</version>
131+
<dependencies>
132+
<dependency>
133+
<groupId>org.testcontainers</groupId>
134+
<artifactId>postgresql</artifactId>
135+
<version>${testcontainers.version}</version>
136+
</dependency>
137+
</dependencies>
138+
<executions>
139+
<execution>
140+
<id>start-database-testcontainer</id>
141+
<phase>generate-sources</phase>
142+
<goals>
143+
<goal>execute</goal>
144+
</goals>
145+
<configuration>
146+
<source>
147+
db = new org.testcontainers.containers.PostgreSQLContainer(
148+
org.testcontainers.utility.DockerImageName.parse("${db.container}").asCompatibleSubstituteFor("postgres"))
149+
.withUsername("${db.username}")
150+
.withDatabaseName("${db.dbname}")
151+
.withPassword("${db.password}");
152+
153+
db.start();
154+
155+
// After you've started the container, collect its generated
156+
// JDBC URL (which contains a random port)
157+
project.properties.setProperty('db.url', db.getJdbcUrl());
158+
</source>
159+
</configuration>
160+
</execution>
161+
</executions>
162+
</plugin>
163+
164+
<!-- initialize database -->
165+
<plugin>
166+
<groupId>org.flywaydb</groupId>
167+
<artifactId>flyway-maven-plugin</artifactId>
168+
<version>${flyway.version}</version>
169+
<dependencies>
170+
<dependency>
171+
<groupId>org.flywaydb</groupId>
172+
<artifactId>flyway-database-postgresql</artifactId>
173+
<version>${flyway.version}</version>
174+
</dependency>
175+
<dependency>
176+
<groupId>org.postgresql</groupId>
177+
<artifactId>postgresql</artifactId>
178+
<version>${postgresql.jdbc.version}</version>
179+
</dependency>
180+
</dependencies>
181+
<executions>
182+
<execution>
183+
<id>initialize-database</id>
184+
<phase>generate-sources</phase>
185+
<goals>
186+
<goal>migrate</goal>
187+
</goals>
188+
<configuration>
189+
<url>${db.url}</url>
190+
<user>${db.username}</user>
191+
<password>${db.password}</password>
192+
<verbose>true</verbose>
193+
<locations>
194+
<location>
195+
filesystem:src/main/resources/db/migration
196+
</location>
197+
</locations>
198+
</configuration>
199+
</execution>
200+
</executions>
201+
</plugin>
202+
203+
<!-- autogenerate jooq code -->
204+
<plugin>
205+
<groupId>org.jooq</groupId>
206+
<artifactId>jooq-codegen-maven</artifactId>
207+
<version>${jooq.version}</version>
208+
<dependencies>
209+
<dependency>
210+
<groupId>org.postgresql</groupId>
211+
<artifactId>postgresql</artifactId>
212+
<version>${postgresql.jdbc.version}</version>
213+
</dependency>
214+
</dependencies>
215+
<executions>
216+
<execution>
217+
<id>generate-jooq-sources</id>
218+
<goals>
219+
<goal>generate</goal>
220+
</goals>
221+
<phase>generate-sources</phase>
222+
<configuration>
223+
<jdbc>
224+
<driver>org.postgresql.Driver</driver>
225+
<url>${db.url}</url>
226+
<user>${db.username}</user>
227+
<password>${db.password}</password>
228+
</jdbc>
229+
<!-- or
230+
<properties>
231+
<property><key>user</key><value>{db-user}</value></property>
232+
<property><key>password</key><value>{db-password}</value></property>
233+
</properties>
234+
-->
235+
<generator>
236+
<database>
237+
<name>org.jooq.meta.postgres.PostgresDatabase</name>
238+
<excludes>
239+
deb_file_hashes
240+
</excludes>
241+
<inputCatalog/>
242+
<inputSchema>public</inputSchema>
243+
</database>
244+
<target>
245+
<packageName>${jooq.target.packageName}</packageName>
246+
<directory>src/main/java</directory>
247+
</target>
248+
</generator>
249+
</configuration>
250+
</execution>
251+
</executions>
252+
</plugin>
253+
254+
<!-- Clean shutdown. Not really required but doesn't hurt -->
255+
<plugin>
256+
<groupId>org.codehaus.gmaven</groupId>
257+
<artifactId>groovy-maven-plugin</artifactId>
258+
<version>2.1.1</version>
259+
<dependencies>
260+
<dependency>
261+
<groupId>org.testcontainers</groupId>
262+
<artifactId>postgresql</artifactId>
263+
<version>${testcontainers.version}</version>
264+
</dependency>
265+
</dependencies>
266+
<executions>
267+
<execution>
268+
<id>start-database-testcontainer</id>
269+
<phase>generate-sources</phase>
270+
<goals>
271+
<goal>execute</goal>
272+
</goals>
273+
<configuration>
274+
<source>
275+
db.stop();
276+
</source>
277+
</configuration>
278+
</execution>
279+
</executions>
280+
</plugin>
281+
</plugins>
282+
</build>
283+
</profile>
284+
</profiles>
285+
</project>

0 commit comments

Comments
 (0)