Boot

Spring Boot with Flyway and PostgreSQL Example

Welcome Java Code Geeks to the Spring Boot with Flyway and PostgreSQL example. In this example we will imagine ourselves as making an application for a car dealership. Well, just a very tiny fraction of it. As we all know, the software we create changes as the business need changes.

PostgreSQL is an object-relational database management system. It’s one of the most popular databases used in the world. Flyway is an open-source database-migration tool, similar to Liquibase.

1. Assumptions

This article is not for the absolute beginner. It is assumed that you know your way around Eclipse. You are familiar with Maven and Spring. This project has been created using Eclipse Oxygen so all instructions are based on this IDE. Basically, this article assumes that you have done some coding. Last but not the least, you must have read and done Java JDBC PostgreSQL Connection Example.

2. Tools

  1. Apache Maven
  2. Eclipse Oxygen
  3. Spring Boot
  4. Java SE Development Kit 8
  5. PostgreSQL download

3. Project Setup

After going through Java JDBC PostGreSQL Connection Example, you should have your PostgreSQL server set up and you should be able to see the records using pgAdmin 4.

Spring Boot with Flyway - View All Rows
View All Rows

Now that our database is set up, let’s connect to it and read the records. Create a Maven project in Eclipse. Our pom.xml should look like the one below:

pom.xml

01
02
03
04
05
06
07
08
09
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
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.javacodegeeks.example</groupId>
  <artifactId>spring-boot-flyway-postgresql</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.9.RELEASE</version>
  </parent>
  
  <properties>
    <java.version>1.8</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    <dependency>
      <groupId>org.postgresql</groupId>
      <artifactId>postgresql</artifactId>
      </dependency>
    </dependencies>
    <build>
      <plugins>
        <plugin>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
      </plugins>
    </build>
</project>

As shown above, our project has 2 dependencies as of the moment. We are using spring-boot-starter-jdbc, which means that we can use all the Spring modules included in it. For example, the Spring Core and Spring JDBC modules will be available for us to use plus many more. The next dependency is PostgreSQL. The Spring Boot Maven plugin enables us to package the project as an executable jar. We will add Flayway later.

4. Initial Java Code

Our code below reads from the database and prints the records. Did you get the class name? Instead of SpringBootPostgreSQL, it’s SummerShoeLandMailSql. Get it? Anyway, the class name is just for fun. In all seriousness, it should be descriptive and meaningful (DAMP) so that it is easy to maintain.

SummerShoeLandMailSql.java

01
02
03
04
05
06
07
08
09
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
package com.javacodegeeks.example;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
@SpringBootApplication
public class SummerShoeLandMailSql implements CommandLineRunner {
    
    @Autowired
    JdbcTemplate jdbcTemplate;
    
    public static void main(String[] args) {
        SpringApplication.run(SummerShoeLandMailSql.class, args);
    }
    @Override
    public void run(String... args) throws Exception {     
        readRecords();
    }
    
    private void readRecords() {
        System.out.println("Reading cars records...");
        System.out.printf("%-30.30s  %-30.30s%n", "Model", "Price");
        jdbcTemplate.query("SELECT * FROM cars", new RowCallbackHandler() {
            public void processRow(ResultSet rs) throws SQLException {
                System.out.printf("%-30.30s  %-30.30s%n", rs.getString("model"), rs.getString("price"));
            }
            
        });
    }
}

Instead of annotating our class with @Configuration, @EnableAutoConfiguration, and @ComponentScan, we use the @SpringBootApplication annotation as a convenient alternative. This annotation tells Spring Boot to scan for other components, add beans based on the classpath, and tags the class as a source of bean definitions.

We implemented the CommandLineRunner because we want to execute the run method after the application context is loaded.

Spring Boot automatically creates JdbcTemplate because we are using the Spring JDBC module (remember the spring-boot-starter-jdbc dependency?). @Autowired automatically loads JdbcTemplate.

The main method uses SpringApplication.run() to run the application.

We then used the readRecords method to execute a SELECT statement and our RowCallbackHandler handles the printing of the movie records. Lastly, the records are printed in a nice column.

5. Initial Code Output

Initial Console Output

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.5.9.RELEASE)
2019-09-03 19:37:56.309  INFO 52212 --- [           main] c.j.example.SummerShoeLandMailSql        : Starting SummerShoeLandMailSql on inspiron-15-7k with PID 52212 (D:\confluence-service\workspace\spring-boot-flyway-postgresql\target\classes started by jpllosa in D:\confluence-service\workspace\spring-boot-flyway-postgresql)
2019-09-03 19:37:56.312  INFO 52212 --- [           main] c.j.example.SummerShoeLandMailSql        : No active profile set, falling back to default profiles: default
2019-09-03 19:37:56.356  INFO 52212 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@587c290d: startup date [Tue Sep 03 19:37:56 BST 2019]; root of context hierarchy
2019-09-03 19:37:57.414  INFO 52212 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
Reading cars records...
Model                           Price                        
mondeo                          £20,000.00                   
mustang                         £40,000.00                   
2019-09-03 19:37:58.667  INFO 52212 --- [           main] c.j.example.SummerShoeLandMailSql        : Started SummerShoeLandMailSql in 2.614 seconds (JVM running for 2.995)
2019-09-03 19:37:58.668  INFO 52212 --- [       Thread-2] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@587c290d: startup date [Tue Sep 03 19:37:56 BST 2019]; root of context hierarchy
2019-09-03 19:37:58.668  INFO 52212 --- [       Thread-2] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

The output shows the contents of the database.

6. Flyway

All of a sudden, the business need has change. Since we are practicing Agile wherein we welcome changing requirement, our application must evolve. Our change requirement is to add a registration number to the car. This is where Flyway comes in. First, let’s add the Flyway dependency to the pom.xml like so:

Flyway artifact

1
2
3
4
<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>

Create the below SQL in your src/main/resources/db/migration directory.

V2__add_registration_number_column.sql

1
ALTER TABLE public.cars ADD registration_number VARCHAR(255);

The naming convention for the migration script is <prefix><version>__<description>.sql. The default prefix is V but may be configured using the flyway.sqlMigrationPrefix property. version is the version number. It should always start with 1 but in our example it is 2. Why is that?. Major and minor versions may be separated by an underscore. description is separated from the version number with a double underscore. As the word says, it’s a description of the migration. We are not starting with version 1 because our database already exists.

Add the line below to application.properties.

application.properties

1
flyway.baselineOnMigrate=true

We have this property because we already have an existing database. This will automatically call baseline when migrate is executed against a non-empty schema with no metadata table. This will initialize the Flyway metadata table.

Let’s run the application again.

Console Output

01
02
03
04
05
06
07
08
09
10
11
12
13
14
2019-09-03 21:48:48.996  INFO 39044 --- [           main] o.f.core.internal.util.VersionPrinter    : Flyway 3.2.1 by Boxfuse
2019-09-03 21:48:49.373  INFO 39044 --- [           main] o.f.c.i.dbsupport.DbSupportFactory       : Database: jdbc:postgresql://localhost:5432/example (PostgreSQL 10.4)
2019-09-03 21:48:49.417  INFO 39044 --- [           main] o.f.core.internal.command.DbValidate     : Validated 1 migration (execution time 00:00.014s)
2019-09-03 21:48:49.449  INFO 39044 --- [           main] o.f.c.i.metadatatable.MetaDataTableImpl  : Creating Metadata table: "public"."schema_version"
2019-09-03 21:48:49.737  INFO 39044 --- [           main] o.f.core.internal.command.DbBaseline     : Schema baselined with version: 1
2019-09-03 21:48:49.765  INFO 39044 --- [           main] o.f.core.internal.command.DbMigrate      : Current version of schema "public": 1
2019-09-03 21:48:49.766  INFO 39044 --- [           main] o.f.core.internal.command.DbMigrate      : Migrating schema "public" to version 2 - add registration number column
2019-09-03 21:48:49.805  INFO 39044 --- [           main] o.f.core.internal.command.DbMigrate      : Successfully applied 1 migration to schema "public" (execution time 00:00.063s).
2019-09-03 21:48:49.893  INFO 39044 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
Reading cars records...
Model                           Price                        
mondeo                          £20,000.00                   
mustang                         £40,000.00                   
2019-09-03 21:48:49.918  INFO 39044 --- [           main] c.j.example.SummerShoeLandMailSql        : Started SummerShoeLandMailSql in 2.073 seconds (JVM running for 2.454)

As the console output shows, Flyway did it’s job. A schema_version is added by Flyway as shown below.

Spring Boot with Flyway - Flyway Schema Version
Flyway Schema Version

As the console output shows, the version 2 migration was applied. Our ALTER TABLE also took effect. The registration_column now exists as shown below.

Registration Number
Registration Number

And the software development cycle continues…

7. Spring Boot with Flyway and PostgreSQL Summary

In summary, we created our database first. Next, we included the spring-boot-starter-jdbc dependency to make available all the Spring modules we needed to make JDBC operations. We then added the database dependency, in this case PostgreSQL. After that, we used JdbcTemplate to perform our SQL commands. Then we set the Flyway baseline which created a table with migration metadata. We then created an SQL file which is used by Flyway to modify our database. We can now picture out how our software evolves.

8. Download the Source Code

This is an example about Spring Boot with Flyway and PostgreSQL.

Download
You can download the source code of this example here: Spring Boot with Flyway and PostgreSQL

Joel Patrick Llosa

I graduated from Silliman University in Dumaguete City with a degree in Bachelor of Science in Business Computer Application. I have contributed to many Java related projects at Neural Technologies Ltd., University of Southampton (iSolutions), Predictive Technologies, LLC., Confluence Service, North Concepts, Inc., NEC Telecom Software Philippines, Inc., and NEC Technologies Philippines, Inc. You can also find me in Upwork freelancing as a Java Developer.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button