DEV Community

Pavan K Jadda
Pavan K Jadda

Posted on

Safely Evolving Database with Liquibase, Spring Data, and Spring Boot

Many implementations out there explaining the process to evolve the database with Liquibase and some of them implemented with Spring framework. I couldnโ€™t find a simple and reliable implementation that explains the whole process.

Update: Added support for Liquibase 4.x and instructions are updated

Overview

This blog post explains the process to safely evolve Database with Liquibase, Spring Boot, and Spring Data JPA. Liquibase Maven plugin gives the ability to perform liquibase operations through maven commands and Liquibase Hibernate plugin helps to generate liquibase changesets based on JPA entities. Code uploaded to Github for reference

Technologies Used

  1. Spring Boot 2.x
  2. Spring Data JPA 2.x
  3. Java 11
  4. Liquibase 4.0.0
  5. Liquibase Hibernate plugin 3.10.1
  6. Liquibase Maven plugin 4.0.0

Instructions

  1. Clone the Github repository into local machine
  2. Add required dependencies for Spring Boot, Spring Data, Liquibase, etc. based on below mentioned pom.xml file

pom.xml

3. Create Model, Controller and JPA Repository interfaces as shown below

Country.Java

package com.liquibasedemo.model;  

import com.fasterxml.jackson.annotation.JsonIgnore;  
import lombok.Data;  

import javax.persistence.\*;  


@Entity  
@Table(name = "country")  
@Data  
public class Country  
{  
    @Id  
    @Column(name = "id")  
    @GeneratedValue(strategy = GenerationType._IDENTITY_)  
    private Long Id;  

    @Column(name = "name")  
    private String name;  

    @Column(name = "code")  
    private String code;  

    @Column(name = "iso\_code")  
    private String isoCode;  


    @ManyToOne  
    @JoinColumn(name = "region\_id")  
    @JsonIgnore  
    private Region region;  

    public Country()  
    {  
    }  

}
Enter fullscreen mode Exit fullscreen mode

CountryRepository.java

package com.liquibasedemo.repo;  

import com.liquibasedemo.model.Country;  
import org.springframework.data.jpa.repository.JpaRepository;  

public interface CountryRepository extends JpaRepository<Country, Long>  
{  
}
Enter fullscreen mode Exit fullscreen mode

CountryController.Java

package com.liquibasedemo.web;  

import com.liquibasedemo.model.Country;  
import com.liquibasedemo.repo.CountryRepository;  
import org.springframework.web.bind.annotation.GetMapping;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.bind.annotation.RestController;  

import java.util.List;  

@RestController  
@RequestMapping("/api/v1/country")  
public class CountryController  
{  
    private final CountryRepository countryRepository;  

    public CountryController(CountryRepository countryRepository)  
    {  
        this.countryRepository \= countryRepository;  
    }  

    @GetMapping(path = "/list")  
    public List<Country> findAllCountries()  
    {  
        return countryRepository.findAll();  
    }  

    @GetMapping(path = "/create")  
    public Country createCountry()  
    {  
        Country country=new Country();  
        country.setCode("USD");  
        country.setIsoCode("USD");  
        country.setName("United States Dollar");  


        return countryRepository.saveAndFlush(country);  
    }  
}
Enter fullscreen mode Exit fullscreen mode

4. Create liquibase.properties under src/main/resources directory with the following content

liquibase.properties

5. Create four profiles in resources directory application-local.yml, application-dev.yml, application-test.yml and application.prod.yml for four different environments.

6. First we generate all of our entity changes in local database using hibernate ddl-auto flag and add the following configuration on application-local.yml file. Then we compare development database against local database and generate change log

jpa:  
  hibernate:  
    ddl-auto: update
Enter fullscreen mode Exit fullscreen mode

7. In src/main/resources directory, create a directory structure as shown below

Directory Structure

In standard liquibase setup we have one db.changelog-master.xml file under resources/db directory and change sets are stored under resources/db/changelog directory.

But in reality we may need to generate separate change sets for each environment as we may not want all the changes in local or development to move to production(or at least thatโ€™s how I do it)

8. For this reason I created three separate db.changelog-master.xml files named db.changelog-dev.xml, db.changelog-test.xml, db.changelog-prod.xml. And respective directories are created under resources/db/changelog/

9. But if want to use same file for all environments just create one db.changelog-master.xml and nothing under resources/db/changelog directory

10. Run the project on local machine and spring boot should create tables for you on database

Local Database Changes

11. Build the project using maven

$ mvn clean package -DskipTests
Enter fullscreen mode Exit fullscreen mode

12. Now letโ€™s generate change set between local and development database using liquibase:diff command as shown below

Liquibase Diff Command

13. Above command compares development database to against local database and generates changeset if it finds any difference. Make sure to include -Pdevor -Ptest or -Pprod flag to select correct profile

14. Maven replaces liquibase.properties file place holders with the supplied parameters in step 10

15. Apply change set to development database using liquibase:update command. This will all changes sets present in resources/db/changelog/dev directory

Liquibase Update Command

16. Now go to the database see all the changes applied and tables created

17. Go to http://localhost:8081/api/v1/country/create to create country and http://localhost:8081/api/v1/country/list to see list of countries

18. Repeat steps 11โ€“15 for Test and Production databases. Use following commands for the same


Code uploaded to Github for reference. Feel free to download and customize it.

Discussion (0)