Database / Liquibase interview questions
What are Liquibase custom change types and how do you create one?
When Liquibase's built-in change types do not cover a specific requirement, you can create a custom change by implementing the CustomChange (or CustomSqlChange for SQL-generating changes) interface. This lets you write Java logic that runs as part of a migration — useful for complex data transformations, calling external APIs during migration, or any operation that requires conditional logic beyond what Liquibase XML can express.
Implementing a custom change:
public class MigrateEncryptedPasswordsChange implements CustomChange { private String tableName; // Liquibase sets this via reflection using <param name="tableName" value="user"/> public void setTableName(String tableName) { this.tableName = tableName; } @Override public void execute(Database database) throws CustomChangeException { try (Connection conn = ((JdbcConnection) database.getConnection()).getUnderlyingConnection()) { // Custom Java logic: read rows, re-encrypt passwords, write back // ... } catch (Exception e) { throw new CustomChangeException("Password migration failed", e); } } @Override public String getConfirmationMessage() { return "Encrypted passwords migrated for table " + tableName; } @Override public void setUp() throws SetupException {} @Override public void setFileOpener(ResourceAccessor resourceAccessor) {} @Override public ValidationErrors validate(Database database) { return new ValidationErrors(); } }
In the changeLog:
<changeSet id="migrate-passwords" author="ivan"> <customChange class="com.example.MigrateEncryptedPasswordsChange"> <param name="tableName" value="user"/> </customChange> </changeSet>
Custom changes must also implement rollback logic if they define a <rollback> block, or they can implement CustomSqlRollback. For SQL-generating changes, implementing CustomSqlChange is preferable because Liquibase can then generate the SQL for the updateSQL preview command.
More Related questions...