Java code changeset in liquibase

Viewed 30860

Is there a way in liquibase to create java code change set (i.e. provide a java class, which will receive a JDBC connection and will perform some changes in the database) ?

(I know that flyway has such feature)

3 Answers

While both answers were very helpful to me, none of them was complete regarding my use case of manipulating data in the database via a database query. I think when you want to use java code in your changeset (instead of plain SQL code) the real point is you want to read and write something from your database (and in between manipulate it in your java code).

In my case I wanted to encrypt all mail addresses stored in our database. I don't know if the following example is perfect but it does its job pretty well:

Part one: add a changelog

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

  <changeSet id="1" author="IssueNo321" runAlways="false" failOnError="true">
    <customChange class="class.package.name.EncryptAllMailAddresses"/>
  </changeSet>
</databaseChangeLog>

Part two: add the changelog to your changelog list db.changelog-master.yaml

databaseChangeLog:
  - includeAll:
      relativeToChangelogFile: true
      path: changelogs/IssueNo321

Part three: implement migration java code

package class.package.name;

import liquibase.change.custom.CustomTaskChange;
import liquibase.database.Database;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.CustomChangeException;
import liquibase.exception.SetupException;
import liquibase.exception.ValidationErrors;
import liquibase.resource.ResourceAccessor;

public class EncryptAllMailAddresses implements CustomTaskChange {

  private EncryptionService encryptionService;

  @Override
  public void execute(Database database) throws CustomChangeException {

    try {
      var dbConn = (JdbcConnection) database.getConnection(); // autocommit is false

    try {
      var selectStatement = dbConn.createStatement();
      var updateStatement = dbConn.createStatement();

      var rs = selectStatement.executeQuery("Select id, email from emails");
      while (rs.next()) {
        var id = rs.getString("id");
        var email = rs.getString("email");

        email = encryptionService.encrypt(email);
        var updateSql = String.format("update emails set email = '%s' where id = %s", email, id);
        updateStatement.execute(updateSql);
      }

      selectStatement.close();
      updateStatement.close();
    } catch (Exception e) {
        throw new CustomChangeException(e);
    }
  }

  @Override
  public String getConfirmationMessage() {
    return null;
  }

  @Override
  public void setUp() throws SetupException {}

  @Override
  public ValidationErrors validate(Database database) {
    return null;
  }

  @Override
  public void setFileOpener(ResourceAccessor resourceAccessor) {
    // do nothing since we don't need additional resources
  }
}
Related