I’m trying to promote liquibase over flyway,
However, we will need to run liquibase directly from Java code (actually kotlin code), since we’re not using spring boot.
Is there something simpler than the command scope API?
And this is the example I have to show and even while promoting it I feel that it is cryptic and verbose: sampleapp/sampleapp.db.liquibase/src/main/java/no/priv/bang/sampleapp/db/liquibase/SampleappLiquibase.java at a284a7d980c836a9f1a60f0fa08b96d8f3787676 · steinarb/sampleapp · GitHub
I googled for Java API and found this blog post from 2024 3 Ways to Run Liquibase | Liquibase
The example in the “How it works” section used the old API that I switched away from a couple of years back.
So I tried it again: Use old liquibase java API to create a schema · GitHub
And:
- The API still shows up as deprecated (javadocs says use commandscope)
- I got the old error that made me switch away from the deprecated API
So tried to simplify to something like Nathan Woxland’s original example from 1 year back i.e. remove the Scope.child(scopedObjects stuff.
The result looks like this
The unit test for the schema works, and the integration test at the end of the build doesn’t fail, but gets some strange error messages at the end
I get an error in the karaf.log as well, it looks lika a classloader issue, maybe…?
It was necessary to keep the Scope.child because that’s where the ClassLoaderResourceAccessor is set.
But the scopeObject only need to contain the ClassLoaderResourceAccessor, there is no need to put the database there.
That meant the code could be made quite a bit smaller and cleaner.
public void applyLiquibaseChangelist(Connection connection, String changelistClasspathResource, ClassLoader classLoader) throws LiquibaseException {
try (var database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection))) {
Scope.child(scopeObjects(classLoader), () -> new CommandScope("update")
.addArgumentValue(DATABASE_ARG, database)
.addArgumentValue(CHANGELOG_FILE_ARG, changelistClasspathResource)
.addArgumentValue(CHANGELOG_PARAMETERS, new ChangeLogParameters(database))
.execute());
} catch (LiquibaseException e) {
throw e;
} catch (Exception e) {
// AutoClosable.close() may throw Exception
throw new LiquibaseException(e);
}
}
private Map<String, Object> scopeObjects(ClassLoader classLoader) {
return Map.of(resourceAccessor.name(), new ClassLoaderResourceAccessor(classLoader));
}
Simplified the example a little bit more: removed unnecessary CommandScope argument CHANGELOG_PARAMETERS:
public void applyLiquibaseChangelist(Connection connection, String changelistClasspathResource, ClassLoader classLoader) throws LiquibaseException {
try (var database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection))) {
Scope.child(scopeObjects(classLoader), () -> new CommandScope("update")
.addArgumentValue(DATABASE_ARG, database)
.addArgumentValue(CHANGELOG_FILE_ARG, changelistClasspathResource)
.execute());
} catch (LiquibaseException e) {
throw e;
} catch (Exception e) {
// AutoClosable.close() may throw Exception
throw new LiquibaseException(e);
}
}
private Map<String, Object> scopeObjects(ClassLoader classLoader) {
return Map.of(resourceAccessor.name(), new ClassLoaderResourceAccessor(classLoader));
}
Hey @SteinarBang - CommandScope still the most “powerful” way to run liquibase, but to make things easier to community we are undeprecating Liquibase class (that now is mostly a wrapper to CommandScope) - High Level API (aka undeprecate Liquibase class) by filipelautert · Pull Request #5988 · liquibase/liquibase · GitHub .
1 Like