Skip to content

Commit b4665dd

Browse files
committed
Initial commit
1 parent 1a9f0ca commit b4665dd

21 files changed

+1232
-0
lines changed

.gitignore

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
##############################
2+
## Java
3+
##############################
4+
.mtj.tmp/
5+
*.class
6+
*.jar
7+
*.war
8+
*.ear
9+
*.nar
10+
hs_err_pid*
11+
replay_pid*
12+
13+
##############################
14+
## Maven
15+
##############################
16+
target/
17+
pom.xml.tag
18+
pom.xml.releaseBackup
19+
pom.xml.versionsBackup
20+
pom.xml.next
21+
pom.xml.bak
22+
release.properties
23+
dependency-reduced-pom.xml
24+
buildNumber.properties
25+
.mvn/timing.properties
26+
.mvn/wrapper/maven-wrapper.jar
27+
28+
##############################
29+
## Gradle
30+
##############################
31+
bin/
32+
build/
33+
.gradle
34+
.gradletasknamecache
35+
gradle-app.setting
36+
!gradle-wrapper.jar
37+
38+
##############################
39+
## IntelliJ
40+
##############################
41+
out/
42+
.idea/
43+
.idea_modules/
44+
*.iml
45+
*.ipr
46+
*.iws
47+
48+
##############################
49+
## Eclipse
50+
##############################
51+
.settings/
52+
#bin/
53+
tmp/
54+
.metadata
55+
.classpath
56+
.project
57+
*.tmp
58+
*.bak
59+
*.swp
60+
*~.nib
61+
local.properties
62+
.loadpath
63+
.factorypath
64+
65+
##############################
66+
## NetBeans
67+
##############################
68+
nbproject/private/
69+
#build/
70+
nbbuild/
71+
dist/
72+
nbdist/
73+
nbactions.xml
74+
nb-configuration.xml
75+
76+
##############################
77+
## Visual Studio Code
78+
##############################
79+
.vscode/
80+
.code-workspace
81+
82+
##############################
83+
## OS X
84+
##############################
85+
.DS_Store
86+
87+
##############################
88+
## Credit
89+
##############################
90+
# File sourced from https://gist.github.com/dedunumax/54e82214715e35439227

README.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Spring Local PostgreSQL
2+
3+
## Description
4+
Provides configuration of an embedded PostgreSQL database (via Testcontainers)
5+
within Spring Boot Applications for local development and testing.
6+
7+
Requires minimal configuration using Spring conventions, but a variety of optional properties are supported to override default behavior.
8+
9+
## Rationale
10+
When developing an Application that uses PostgreSQL in production, an embedded PostgreSQL server provides the benefits of using an in-memory database, like H2, but avoids the downsides. The database is spun up and torn down when the Application starts up and shuts down, but developers are able to utilize PostgreSQL features (that alternatives like H2 may not support) and the test and local environments better resemble production. Development is more effective and reliable.
11+
12+
While Testcontainers is meant to be used exclusively in support of integration tests, this module is designed to support running the Application locally as well, reducing the overhead of maintaining Testcontainers and some other solution that fundamentally does the same thing. There is no need to maintain a local PostgreSQL server nor additional Docker configuration inside or outside the project.
13+
14+
## Requirements
15+
### Java 17
16+
https://adoptium.net/temurin/releases/?version=17
17+
18+
### Docker
19+
https://www.docker.com/products/docker-desktop/
20+
21+
### PostgreSQL JDBC Driver
22+
https://jdbc.postgresql.org<br>
23+
```xml
24+
<dependency>
25+
<groupId>org.postgresql</groupId>
26+
<artifactId>postgresql</artifactId>
27+
<scope>runtime</scope>
28+
</dependency>
29+
```
30+
31+
### Testcontainers PostgreSQL
32+
Version `1.18.3` is included as a transitive dependency.<br>
33+
https://www.testcontainers.org/modules/databases/postgres
34+
```xml
35+
<dependency>
36+
<groupId>org.testcontainers</groupId>
37+
<artifactId>postgresql</artifactId>
38+
<version>1.18.3</version>
39+
</dependency>
40+
```
41+
42+
### Spring Boot 3
43+
A variety of modules from version `3.1.2` are included as transitive dependencies.<br>
44+
https://spring.io/projects/spring-boot
45+
```xml
46+
<dependencyManagement>
47+
<dependencies>
48+
<dependency>
49+
<groupId>org.springframework.boot</groupId>
50+
<artifactId>spring-boot-dependencies</artifactId>
51+
<version>3.1.2</version>
52+
<type>pom</type>
53+
<scope>import</scope>
54+
</dependency>
55+
</dependencies>
56+
</dependencyManagement>
57+
```
58+
```xml
59+
<dependencies>
60+
<dependency>
61+
<groupId>org.springframework.boot</groupId>
62+
<artifactId>spring-boot-starter-web</artifactId>
63+
</dependency>
64+
<dependency>
65+
<groupId>org.springframework.boot</groupId>
66+
<artifactId>spring-boot-configuration-processor</artifactId>
67+
</dependency>
68+
</dependencies>>
69+
```
70+
## Usage
71+
72+
### Configuration
73+
74+
First, add this module as a dependency in your project:
75+
```xml
76+
<dependency>
77+
<groupId>io.github.quinnandrews</groupId>
78+
<artifactId>spring-local-postgresql</artifactId>
79+
<version>1.0-SNAPSHOT</version>
80+
</dependency>
81+
```
82+
83+
Next, add `@EnableLocalPostreSQL` to your Spring Boot Application Class:
84+
```java
85+
@EnableLocalPostgreSQL
86+
@SpringBootApplication
87+
public class Application {
88+
89+
public static void main(final String[] args) {
90+
SpringApplication.run(Application.class, args);
91+
}
92+
}
93+
```
94+
95+
Then define the following property for the profiles that should initialize the embedded PostgreSQL:
96+
```properties
97+
spring.local.postgresql.engaged=true
98+
```
99+
100+
If desired, define any of the following properties to override default behavior (see below for detailed descriptions):
101+
102+
```properties
103+
spring.local.postgresql.container.image=postgres:15
104+
spring.local.postgresql.container.port=15432
105+
spring.local.postgresql.container.log.follow=true
106+
spring.local.postgresql.database.name=local
107+
spring.local.postgresql.database.username=superuser
108+
spring.local.postgresql.database.password=password
109+
spring.local.postgresql.database.application.username=appuser
110+
spring.local.postgresql.database.application.password=password
111+
spring.local.postgresql.database.init.script=data/init.sql
112+
```
113+
114+
### Configuration as a Test Dependency
115+
Understandably, one may prefer this module as a test dependency rather than a compile dependency. But this means that all configuration for this module can only reside in your project's test source, which is fine for integration tests, but how, then, can you run the Application locally?
116+
117+
To do this we implement an approach surfaced in this [article](https://bsideup.github.io/posts/local_development_with_testcontainers/) by Sergei Egorov.
118+
119+
First, add this module as a test dependency in your project:
120+
```xml
121+
<dependency>
122+
<groupId>io.github.quinnandrews</groupId>
123+
<artifactId>spring-local-postgresql</artifactId>
124+
<version>1.0-SNAPSHOT</version>
125+
<scope>test</scope>
126+
</dependency>
127+
```
128+
Next, add a Spring Boot Application Class to your project's test source that includes the `@EnableLocalPostgreSQL` Annotation and passes the Application Class in your project's main source to the `run` method (so that Configuration in the main source is initialized):
129+
```java
130+
@EnableLocalPostgreSQL
131+
@SpringBootApplication
132+
public class LocalDevApplication {
133+
134+
public static void main(final String[] args) {
135+
SpringApplication.run(Application.class, args);
136+
}
137+
}
138+
```
139+
140+
Then you can reference `LocalApplication.class` in your integration tests to enable the embedded PostgreSQL by using the `classes` attribute on `@SpringBootTest`:
141+
```java
142+
@ActiveProfiles("test")
143+
@SpringBootTest(classes = LocalApplication.class)
144+
class IntegrationTest {
145+
146+
}
147+
```
148+
149+
Or, alternatively, your integration tests can ignore `LocalApplication.class` and enable the embedded PostgreSQL by using `@EnableLocalPostgreSQL`:
150+
```java
151+
@EnableLocalPostgreSQL
152+
@ActiveProfiles("test")
153+
@SpringBootTest
154+
class IntegrationTest {
155+
156+
}
157+
```
158+
Then you can create a `Run Configuration` in IntelliJ IDEA, for example, setting `LocalApplication.class` as the `Main Class` and defining `local` as the active profile.
159+
160+
Now when you run the `Run Configuration` or your integration tests, the embedded PostgreSQL will be initialized, assuming that the `local` and `test` profiles have set the `spring.local.postgresql.engaged` property to `true'.`
161+
162+
**But there is a way to make this even more convenient.**
163+
164+
If your integration tests reference `LocalApplication.class`, it's worth noting that the `main` method isn't actually executed in that case. Spring is doing something different.
165+
166+
What this means is that you can activate the `local` profile explicitly in the body of the `main` method:
167+
```java
168+
@EnableLocalPostgreSQL
169+
@SpringBootApplication
170+
public class LocalApplication {
171+
172+
public static void main(final String[] args) {
173+
final var springApplication = new SpringApplication(Application.class);
174+
springApplication.setAdditionalProfiles("local");
175+
springApplication.run(args);
176+
}
177+
}
178+
```
179+
180+
Now you can run the Application locally in IntelliJ IDEA by simply right-clicking on `LocalApplication.class` in the Project Panel and selecting `Run 'LocalApplication'` from the Context Menu, which will also create a `Run Configuration` for later use.
181+
182+
### Supported Configuration Properties
183+
184+
spring.local.postgresql.engaged
185+
: Whether the embedded, containerized PostgreSQL database should be configured to start when the Application starts. By default, it is not engaged. Only engaged if `true`.
186+
187+
spring.local.postgresql.container.image
188+
: The name of a Docker Image containing a given version of PostgreSQL (example: `postgres:15`). If undefined, the Testcontainers default of `postgres:9.6.12` is used.
189+
190+
spring.local.postgresql.container.port
191+
: The port on the Docker Container that should map to the port used by PostgreSQL inside the container. If undefined, a random port is used, which is preferred for integration tests, but when running the Application locally, defining a fixed port is useful. It gives developers the ability to configure a JDBC client with a consistent port. Otherwise, the port in the client's configuration must be updated if the Application had been restarted since the client was last used.
192+
193+
spring.local.postgresql.container.log.follow
194+
: Whether the Application should log the log output produced by the Container. By default, container logs are not followed. Set with `true` to see their output.
195+
196+
spring.local.postgresql.database.name
197+
: The name of the PostgreSQL database the Application will connect to. If undefined, defaults to the Testcontainers default of `test`.
198+
199+
spring.local.postgresql.database.username
200+
: The username of an admin or superuser in the PostgreSQL database. If no `spring.local.postgresql.database.application.username` is defined, this will also be the username the Application uses to connect. If undefined, defaults to the Testcontainers default of `test`.
201+
202+
spring.local.postgresql.database.password
203+
: The password that goes with the username of the admin or superuser in the PostgreSQL database. If no `spring.local.postgresql.database.application.username` is defined, this will also be the password for the username the Application uses to connect. If undefined, defaults to the Testcontainers default of `test`.
204+
205+
spring.local.postgresql.database.application.username
206+
: In most cases the database user used by the Application should not have admin or superuser privileges. This property provides the ability to define the username of an "application user" for use during testing and local development. The Application will use this username to connect to the PostgreSQL database. If undefined, the value defined by `spring.local.postgresql.database.username` will be used instead. NOTE: This application user is NOT automatically created. An init-script is required to create the user and grant their initial privileges.
207+
208+
spring.local.postgresql.database.application.password
209+
: In most cases the database user used by the Application should not have admin or superuser privileges. This property provides the ability to define the password for the username of an "application user" for use during testing and local development. The Application will use this password to connect to the PostgreSQL database. If undefined, the value defined by `spring.local.postgresql.database.password` will be used instead. NOTE: This application user is NOT automatically created. An init-script is required to create the user and grant their initial privileges.
210+
211+
spring.local.postgresql.database.init.script
212+
: The path to an SQL file (beginning with the `resources` directory as the root) that should be executed when the Docker Container starts. Executes before migrations. Useful for administrative tasks, like creating additional users, for example. If undefined, no script is executed.
213+
214+
## Examples
215+
216+
The test source contains an example Application as well as integration tests that test against it. They can be referenced as examples, if needed.
217+
218+
However, a more robust, true to life example will be provided in the near future.
219+
220+
## Roadmap
221+
1) **Provide Robust Example**
222+
2) **Support Option to Activate pgAdmin.**
223+
3) **Support Testcontainers Reuse Property.**

0 commit comments

Comments
 (0)