Liquibase: How to identify change set only basis ID? - java

As per the liquibase documentation:
Each changeSet tag is uniquely identified by the combination of the
“id” tag, the “author” tag, and the changelog file classpath name.
This seems to be a very poor design choice. The identity of a changeset shouldn't be linked to its location. If the changelog is run via automatic application deployment the changeset would come from a classpath location within a JAR file. If I want to run the same changesets from commandline manually, the location might be the current directory.
In this case instead of recognizing the changeset as same based on its ID liquibase will try to apply it twice. Is there a way to change this behavior and have it identify changesets only basis specified ID?

I would suggest using the logicalFilePath attribute of databaseChangeLog tag.
This gives you more freedom to change the directory structure of your project.
Also it prevents the file name from being stored as an absolute path (which might happen in some circumstances).

#binoternary's answer works. But the problem is that logicalFilePath is only available in XML changesets whereas I was using SQL changesets. The work around is to create a XML changeset and then include SQL into it like this:
<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="new-tables" author="kshitiz" logicalFilePath="new_tables.sql">
<sqlFile path="new_tables.sql" relativeToChangelogFile="true" />
</changeSet>
</databaseChangeLog>

Only if you manipulate the sourcecode and recompile your own version of liquibase.
Actually the design is fine, you just use it wrongly.
If you e. g. have a big team where each team maintains their changesets in a separate liquibase file, it would be fatal to not take the filename into account, as different teams could use the same ID.
Just make sure you are calling Liquibase always the same way and the identities of the changesets will not change.

Related

How to fire liquibase rollback script from spring application

I am using spring with liquibase to update my database. Since know I have not need to user rollback functonality, but the times come where I would like to make it work.
But I cant seems to fire it from my application.
I know that maven has plugin which helps with that, but until know I was not using it and when I add it I need to provide source and credentials to my database.
In this moment liquibase is configured in xml.
<bean id="liquibase" class="liquibase.integration.spring.SpringLiquibase">
<property name="dataSource" ref="p6spyDataSource"/>
<property name="changeLog" value="classpath:db.changelog-master.yaml"/>
.
.
</bean>
And in maven I have only dependency to liquibase-core.
And the place where I set liquibase.shouldRun is in application.properties
DataSource is taken from TomEE configuration server.xml file
So the question is if I can maybe somehow add maven plugin without adding credentials (should be taken from dataSource). Or is there other way to run rollback script from my changelog?
There are several related questions that have been posted previously about using Liquibase rollback with Spring Boot. This one seems the most similar to your post: Perform a liquibase:rollback from the command line when properties are in Spring-boot files (application.properties) and not liquibase.properties
Here is the answer as provided by Robert Kleinschmager:
The property names within springs application.properties and liquidate.properties are not compatible. You have three options
#1 just create a separate liquibase.properties file with the content you need - see liquibase doc as you only need to fix your current setup
#2 give the database parameters via command-line arguments
mvn liquibase:rollback -Dliquibase.rollbackCount=1 -Dliquibase.url=jdbc:postgresql://localhost:5432/comptesfrance -Dliquibase.username
see rollback goal for all arguments
#3 if you need a permanent solution, then you may add the liquibase properties into your application.properties and reuse them in the same file. i.e.
liquibase.url=jdbc:postgresql://localhost:5432/comptesfrance
spring.datasource.url=${liquibase.url}

xerces jar name changes behaviour

I have an older application that uses a jar named xerces.jar. Looking at this I can see that it is the same as xercesImpl-2.9.1.jar from the maven repository.
When I run the application the xml validation behaves differently depending on if the jar is named xerces.jar or xercesImpl-2.9.1.jar.
I suspect there is something going on with the order that it it found on the classpath but I don't fully understand what is going on.
Any ideas as to what is going on?
The particular change is this:
The start tag looks like:
<Message xmlns="http://www.foo.com/messaging" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" release="006" version="010" xsi:schemaLocation="file:///C:/SOME/PATH">
When the jar is named "xerces.jar" this accepted when the app validates against a specific schema. When the jar is named "xercesImpl-2.9.1.jar" then it gives the error: Attribute 'xsi:schemaLocation' is not allowed to appear in element

Generating a changelog for an empty database with Liquibase in offline mode

As the title says, I want to use Liquibase to generate a changelog for an empty database in offline mode. My ant file looks like the following:
<project xmlns:liquibase="antlib:liquibase.integration.ant">
<taskdef resource="liquibase/integration/ant/antlib.xml" uri="antlib:liquibase.integration.ant">
<classpath>
<pathelement location="antlibs/liquibase-core-3.5.3.jar" />
<pathelement location="antlibs/snakeyaml-1.18.jar" />
</classpath>
</taskdef>
<target name="generate-changelog">
<liquibase:generateChangeLog>
<liquibase:database url="offline:postgresql?snapshot=snapshot.json" />
<liquibase:xml outputfile="target/changelog.xml" encoding="UTF-8" />
</liquibase:generateChangeLog>
</target>
Later I want to add Liquibase-Hibernate4 to generate the changelog from my entities, but I encounter various issues beforehand. If I simply use the url "offline:postgresql", the ant task crashes with a NullPointerException, as Liquibase tried to clone the snapshot which is not available. If I add "?snapshot=snapshot.json", Liquibase (or Yaml) is not able to find my file.
My questions:
Is this the right approach when I simply want to create the changelog for my entities without any base snapshot at all?
Is there an easier way to provide liquibase with an empty database as base?
How do I add the snapshot.json to the project to make sure, that the ant task can find it?
In the meantime I was able to find out some things:
To make sure that snapshot.json can be found by the task, one has to put it into a jar file and add the jar file to the task's class path.
It does not seem to be the right approach for the Hibernate extension, as it seems that the Hibernate extension does not work with offline URLs.
According to https://liquibase.jira.com/browse/CORE-2183, a possible workaround would be to use an empty H2 database as a base to generate the initial JPA changelogs.

liquibase 3.5.X can't find any files for includeAll with relative path

We are using liquibase 3.4.2 and want to update to 3.5.3 but all my attempts failed because liquibase doesn't find any file which are included by using includeAll. I have tested liquibase 3.5.0, 3.5.1 and 3.5.3 (I skipped 3.5.2 because of this blog post).
My ChangeSet looks like this:
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">
<includeAll path="relative/dir1" relativeToChangelogFile="true" errorIfMissingOrEmpty="true"/>
<includeAll path="relative/dir2" relativeToChangelogFile="true" errorIfMissingOrEmpty="true"/>
</databaseChangeLog>
My directory structure (inside a jar which is included in a war) looks like this:
/some/dir/changeset.xml (the code above)
/some/dir/relative/dir1/another-changeset.xml
/some/dir/relative/dir2/another-changeset-1.xml
/some/dir/relative/dir2/another-changeset-2.xml
I have already debugged through liquibase and got stuck at ClassLoaderResourceAccessor.java:108:
if (entry.getName().startsWith(path)) {
In my case entry.getName() returns some in the first loop, then some/dir and so on till some/dir/relative/dir1/another-changeset-1.xml, some/dir/relative/dir2/another-changeset-1.xml and some/dir/relative/dir2/another-changeset-2.xml. But the condition is always false because path contains something like jar:file:/C:/path/to/maven/project/war/target/example.war-1.0-SNAPSHOT/WEB-INF/lib/changesets-1.0-SNAPSHOT.jar!/relative/dir1/ or jar:file:/C:/path/to/maven/project/war/target/example.war-1.0-SNAPSHOT/WEB-INF/lib/changesets-1.0-SNAPSHOT.jar!/relative/dir2/
Is this really a bug in liquibase since 3.5.0? It works perfectly if I downgrade to liquibase 3.4.2. It works also if I use include instead of includeAll but in my real application I have much more changesets and I don't want to list them all manually.
I have found some information on this, but none of them helps me. For the sake of completeness:
Liquibase-JIRA: https://liquibase.jira.com/browse/CORE-2851, https://liquibase.jira.com/browse/CORE-2863, https://liquibase.jira.com/browse/CORE-2898, https://liquibase.jira.com/browse/CORE-2974
SO: Liquibase includeAll tag is ignored, includeAll path="" not working in 3.5.3, using java -jar method
Try this,
<includeAll
path="file:/absolute/path/to/changeset/folder"
relativeToChangelogFile="false" />
Start the path with file:/ and use an absolute path. This is not ideal, but I was able to get liquibase-maven-plugin 3.6.3 to load the changeset.

How do I use templating with Hibernate and Gradle?

I have a server that talks to a database that I need to test. I connect to the database using Hibernate and manage the dependencies using Gradle. I want to use separate tables in MySql for production and testing. So I have currently this line in hibernate.cfg.xml:
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/production_database</property>
But what I really want is for it to be something like:
<property name="hibernate.connection.url">jdbc:mysql://127.0.0.1:3306/${DATABASE_NAME}</property>
and then when I run gradle test, DATABASE_NAME can be set to "test_database_name", and when I run gradle jettyRun it'll still be "production_database". This seems like something that should be possible, but when I google for "hibernate teplating" I get references this other thing called HibernateTemplate that has nothing to do with what I want as far as I can tell. What's the syntax that'll make this happen for me?
You should move that property out of hibernate.cfg.xml, and into a database.properties file.
And, Then you can use gradle to modify this file depending upon the argument.
Please refer to Gradle Tasks for this.
ant.propertyfile(
file: "database.properties") {
entry( key: "connectionurl", value: "somevalue")
}

Categories