An in-memory database may be sufficient during the early phase of project development. However, sooner or later you will need to persist some data. Read this post to learn how to configure a Spring Boot app to use a PostgreSQL database.
For this article I’m going to use a Spring Boot project with one example endpoint. I will configure it to connect to a PostgreSQL database run from a docker container.
You can find the example project used in this post in the spring-boot-postgres-flyway repository.
Our example REST API offers one endpoint that enables persisting data about bakery goods. The application relays on a PostgreSQL database that can be instantiated using the docker container that is included in the project. We use profile specific properties to separate configuration applied on the development environment. To examine the SQL commands run automatically against the database I generated the schema.sql
file with the DDL script.
You can see the project directory tree on the image below:
Spring Boot 2.2.5
project with the Web
and JPA
dependencies. You can read about setting up a similar project with Spring Initializr in How to create a new Spring Boot Project post.$ java --version
openjdk 11.0.6 2020-01-14
OpenJDK Runtime Environment (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1)
OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1, mixed mode, sharing)
We are going to instruct our app to work with a running PostgreSQL database. If you are using the docker-compose.yml
file included in the project, don’t forget to start the container with the database instance.
Add the dependency for the driver for PostgreSQL database to your pom.xml
file:
<!--pom.xml-->
…
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
…
Connecting your application to a database requires providing credentials and a database url.
The values from application.properties
will be used unless they are overridden. In the default properties I specified the database dialect (optional):
# src/main/resources/application.properties
…
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
If you place here values used for connecting to the local database, the app will work on your machine. In case you forget to supersede them on the production, the app won’t be able to connect to the “real” database.
In my app I’m setting the profile-specific properties to make sure that none of the development properties will be applied to the production. I separated the attributes by creating two files: application.properties
and application-dev.properties
.
The properties for the PostgreSQL database run on my development environment looks like on the snippet below:
# src/main/resources/application-dev.properties
spring.jpa.properties.javax.persistence.schema-generation.database.action=create
…
spring.datasource.url=jdbc:postgresql://localhost:5432/springbootpostgresflyway
spring.datasource.username=db
spring.datasource.password=admin
Now I have to set the active profile. I can do this in two ways. One relays on defining it in the default properties file:
# src/main/resources/application.properties
spring.profiles.active=dev
If several profiles are specified, a last-wins strategy applies. For example, profiles specified by the
https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-profile-specific-propertiesspring.profiles.active
property are added after those configured through theSpringApplication
API and therefore take precedence.
I have to remember to override the active profile when running the app on production.
Instead of defining the active profile in the application.properties
file, you can edit the application configuration using your IDE. In IntelliJ, select Run → Edit Configuration
from the menu and set the profile like on the image below:
Thanks to this, the development properties will be applied only on your machine.
Below you will find description of the options used in my example and also some alternatives that you can apply in your projects.
The target database is auto-detected by default. You can specify the proper value on your own, as we can read in the docs:
The dialect to use is detected by the JPA provider. If you prefer to set the dialect yourself, set the
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-configure-jpa-propertiesspring.jpa.database-platform
property.
url
is the JDBC URL of the database. Remember to keep the port, database name and credentials consistent with the database configuration. Make sure that the credentials from the dev environment are not accidentally used for the production.
We use this property to define which action should be performed automatically on application startup. The default value is none
, available options are listed in the documentation.
Use this property alongside with the schema.generation.script properties if you want to save the DDL schema to a file when starting the app. |
You should follow a single schema generation approach. You can learn more in the Schema generation properties not well documented issue.
Instead of the javax.persistence.schema-generation.database.action
property you could use the spring.jpa.hibernate.ddl-auto
property to apply the JPA features for DDL generation on the application startup. The standard values are none
, validate
, update
, create
, and create-drop
.
Be aware of differences in the default value for this property. As we can read in the Spring Boot documentation:
The
https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-configure-jpa-propertiesspring.jpa.hibernate.ddl-auto
is a special case, because, depending on runtime conditions, it has different defaults. If an embedded database is used and no schema manager (such as Liquibase or Flyway) is handling theDataSource
, it defaults tocreate-drop
. In all other cases, it defaults tonone
.
You can now start the application and verify that it is connected to the database. In my example, Hibernate automatically creates tables based on my entities.
I store the DDL script in a file so I can easily examine the commands that were run against the database on application startup. Check out the Save Hibernate DDL schema to a file post to learn how you can do the same. The schema.sql
file generated in my project looks like this:
-- schema.sql
create table cookie (
id bigserial not null,
flavour varchar(255),
primary key (id)
)
The file was not added to my repository. It will be generated in the project directory when you start your application.
You can see the database schema on the screenshot taken from my IntelliJ on the image below:
The work done in this post in contained in the commit 84a88ff03f8b547d0a0fc608fa99586cc3f62cdc.
You can keep recreating container and volume with the database content until you are satisfied with the outcome and all entities are polished. Once the schema is ready you can remove the property responsible for creating the tables – the javax.persistence.schema-generation.database.action
property.
However, the schema will evolve during the project lifetime, so it is a good idea to implement a better solution for maintaining it. You can learn how to add migrations to this project in the Add the Flyway migrations to a Spring Boot project post.
If you are transferring the app from the in-memory database (e.g. h2) remember to adjust all entities to the target database requirements – they will be more strict. For example, you can encounter problems with reserved keywords used in the entities. Be sure to check out the database documentation to verify that no conflicts will arise during the database transition.
Spring Boot allows us to set some vendor-independent configuration options provided by Spring Data JPA as external configuration properties for Hibernate. You can find the JPA properties in the documentation. Furthermore, you can check out this appendix with a list of common Spring Boot properties. When a property name is long it is easy to misspell it.
Make sure that the docker image version provides you with the database version that you actually want for the application.
Photo by Lucas Allmann on StockSnap
Spring Security allows us to use role-based control to restrict access to API resources. However,…
A custom annotation in Spring Boot tests is an easy and flexible way to provide…
Delegating user management to Keycloak allows us to better focus on meeting the business needs…
Swagger offers various methods to authorize requests to our Keycloak secured API. I'll show you…
Configuring our Spring Boot API to use Keycloak as an authentication and authorization server can…
Keycloak provides simple integration with Spring applications. As a result, we can easily configure our…