When I wrote about release v4.0 of the JHipster Mini-Book, I said I planned to update 21-Points Health in the near future. I started looking into the process Tuesday morning, and I'm happy to say I finished updating everything by early Wednesday morning.
To give you some background, the codebase on www.21-points.com was running AngularJS + Spring Boot from the first version I wrote with JHipster 2.x (October 2015). When I wrote
the 2nd edition of the JHipster Mini-Book, I changed the entity relationships so the preferences table contained a user_id column instead of adding a preferences_id to the jhi_user table. I never deployed that codebase because everything was running smoothly in production.
My strategy to upgrade from the exiting code/database to v4 was fairly simple:
- Export the production database to a local instance.
- Figure out what needed to change between the new schema and the old one.
- Use Liquibase to make the necessary changes.
- Deploy the new codebase, migrating the database on startup.
To export the production database to a local database, I started with Heroku's import/export docs for PostgreSQL. I found pg:pull and went to work.
heroku pg:pull HEROKU_POSTGRESQL_GRAY_URL health --app health-by-points
This created a local health database that contained my production schema and data. I ended up dropping and re-importing the production database several times. This Heroku feature came in very handy!
Next, I downloaded Liquibase and installed it in /opt/tools. I ran the following command to copy the PostgreSQL JDBC driver from my local Maven repo to Liquibase's classpath.
[mraible:/opt/tools/liquibase-3.5.3-bin] % cp ~/.m2/repository/org/postgresql/postgresql/9.4.1212/postgresql-9.4.1212.jar lib/.
I set up a new local database with 21-Points v4's configuration in my application-prod.yml:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: jdbc:postgresql://localhost:5432/twentyonepoints
username: twentyonepoints
password: 21points
I ran Liquibase to generate an XML changelog for the differences between the two databases:
./liquibase \ --driver=org.postgresql.Driver --url=jdbc:postgresql://localhost:5432/health --username=postgres \ diffChangeLog \ --referenceUrl=jdbc:postgresql://localhost:5432/twentyonepoints \ --referenceUsername=twentyonepoints --referencePassword=21points
I copied the results to a ${date}-migrate_from_v1.xml file in src/main/resources/config/liquibase/changelog and added a reference to it in src/main/resources/config/liquibase/master.xml. I updated application-prod.xml to point to the production database that I downloaded from Heroku.
I started 21-Points with ./gradlew -Pprod and watched everything fail:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in
class path resource [org/jhipster/health/config/DatabaseConfiguration.class]: Invocation of init method failed;
nested exception is liquibase.exception.ValidationFailedException: Validation Failed:
2 change sets check sum
config/liquibase/changelog/00000000000000_initial_schema.xml::00000000000000::jhipster
was: 7:eda8cd7fd15284e6128be97bd8edea82 but is now: 7:a6235f40597a13436aa36c6d61db2269
config/liquibase/changelog/00000000000000_initial_schema.xml::00000000000001::jhipster
was: 7:e87abbb935c561251405aea5c91bc3a4 but is now: 7:29cf7a7467c2961e7a2366c4347704d7
I posted my issue to Stack Overflow and kept trying to solve it. Googling the error led me to a SQL statement I could use to clear the checksums:
update databasechangelog set md5sum=null;
I ran this on a fresh pull and started my app again. This time it failed when trying to create the first table.
2017-10-24 12:12:02.670 ERROR 22960 --- [ main] liquibase : classpath:config/liquibase/master.xml: config/liquibase/changelog/20160831020048_added_entity_Points.xml::20160831020048-1::jhipster: Change Set config/liquibase/changelog/20160831020048_added_entity_Points.xml:: 20160831020048-1::jhipster failed. Error: ERROR: relation "points" already exists [Failed SQL: CREATE TABLE public.points
To get past this, I found I had to add preconditions to many of my existing Liquibase files to prevent tables and foreign constraints from being created. You can see all the changes I needed to make in PR #27 on GitHub. Migrating from the old database to the new one was powered by Liquibase and worked great!
Once I had everything migrating the database successfully, I grabbed a cold Coors out of the fridge, and decided it was time to go to production. I asked Trish if she thought it was a good idea. She was honest: "No". I went for it anyway.
I used Heroku's Dashboard to turn on maintance mode, ran the heroku-deploy.sh script, and started everything back up. It worked! 🤗
This was a great experience and I learned a bunch by doing it. For your viewing pleasure, here's some screenshots of the new version.
For comparison's sake, here's the same screens from v1.
I hope you enjoyed this story about migrating a JHipster app from v2.x to v4.x in production. If you have similar migration stories, I'd love to hear about them!







