3

Trying to run Java-based (compiled) migration files which are not in the project where Flyway is configured. Can anyone tell me is it possible to do so?

I've created a jar which use flyway to do migrations. Jar expects an argument which is migration scripts' location. Migration scripts are in a different location/project. So far all scripts are SQL based. (i.e. XXX.sql). Need to add a java based migration scripts to it, to do some complex logic.

Tried to add pom.xml to the script location and a sample java migration script in db/migration folder. Java-based migration file is ignored by Flyway. Is it due to checksum validation fail?

Java-based migrations are compiled and .class files are in the classpath. My folder structure as below.

C:/
└── database-migration-scripts
    └── src/main/java/
        └── db
            └── migration
                └── V1__m1.sql
                └── V2__m2.sql
                └── V3__SampleJava_script.java
    └── target/classes/
        └── db
            └── migration
                └── V3__SampleJava_script.class
    └── pom.xml

W:/
└── someLocationOutsideProject
    └── flyway-database-migration.jar

NOTE: flyway runner (jar) and scripts will be in different locations in the same or different machine. In the example above, migration scripts in C:/ and jar in W:/

Vinni
  • 569
  • 1
  • 8
  • 18
  • Your Java migration scripts must be compiled and in the classpath. You cannot just put the Java source in. – Henry Jun 01 '18 at 04:22
  • I updated my question. I've `.class` files in the classpath of my database-migration-scripts. Is that what you meant? Or you mean that it should be in the classpath of the flyway-database-migration.jar file? – Vinni Jun 01 '18 at 04:32
  • When flyway executes, it must find these classes in its classpath. I cannot tell you more as I don't know what your `flyway-database-migration.jar` does exactly. – Henry Jun 01 '18 at 04:38
  • Please find jar contents [here](https://github.com/Vinni389/flyway-database-migration). And my migration scripts [here](https://github.com/Vinni389/database-migration-scripts/blob/master/README.md) – Vinni Jun 03 '18 at 00:40
  • Updated my question. Tried placing java migration files in `src/main/java/db/migration`. No luck. – Vinni Jun 03 '18 at 01:55

1 Answers1

1

In order to be discovered, Java migrations should go under src/main/java with the package db.migration

java migration location

e.g.

package db.migration; // <-- classpath:db/migration

import org.flywaydb.core.api.migration.jdbc.JdbcMigration;
import java.sql.Connection;

public class V3__SampleJava_script implements JdbcMigration {
    public void migrate(Connection connection) throws Exception {
        // your code...
    }
}

Difficult to diagnose without seeing your pom.xml and how your jar is packaged but given the folder structure of your target directory above, perhaps either the V3__SampleJava_script.class is added to the jar under classpath:resources/db/migration or is just not included at all.

To check, try unzipping the jar file:

jar -xf flyway-database-migration.jar

or just listing the contents:

jar -tf flyway-database-migration.jar

It is also worth noting that if you have overridden the locations setting with a filesystem: path, the documentation states that the directory "may only contain sql migrations", any java migrations will just be ignored.


Update 2018-06-03

Given that the flyway-database-migration.jar is being pointed to a filesystem: location, only sql migrations will be discovered, not java ones. The database-migration-scripts directory needs to be added to the classpath and the flyway location set to classpath:db/migration.

java -cp C:\database-migration-scripts;<existing classpath> ...

Update 2018-06-09

Because of the way that the flyway-database-migrations.jar is packaged, using the Spring Boot Executable Jar format, all the application dependencies are placed in the BOOT-INF/lib directory inside the executable jar and are loaded by a separate classloader from the org.springframework.boot.loader.JarLauncher main class. So, contrary to the above, I'm not sure if it's possible to pass additional classpath entries to the application using the -cp command line option. I think you would need to remove spring boot and package it as a regular jar file. I certainly encountered class visibility issues and decided to try a different approach.

I downloaded the Flyway Command Line Runner and updated the <RUNNER_DIR>/conf/flyway.conf settings with the following:

flyway.url=jdbc:mariadb://localhost:3306/flyway?createDatabaseIfNotExist=true
flyway.user=root
flyway.password=yourPassword
flyway.locations=classpath:db/migration

I created a directory under <RUNNER_DIR>/jars/ called database-migration-scripts.jar with the following structure (the .jar is important as Flyway will only add files or directories with this suffix to the classpath):

database-migration-scripts.jar/
└── db
    └── migration
        ├── V1__m1.sql
        ├── V2__m2.sql
        └── V3__SampleJava_script.class

Finally, I added all of the runtime dependencies for the database-migration-scripts project to <RUNNER_DIR>/lib:

lib/
├── animal-sniffer-annotations-1.14.jar
├── checker-compat-qual-2.0.0.jar
├── checker-qual-2.3.0.jar
├── error_prone_annotations-2.1.3.jar
├── flyway-commandline-5.1.1.jar
├── flyway-core-5.1.1.jar
├── guava-23.6-jre.jar
├── j2objc-annotations-1.1.jar
├── jcl-over-slf4j-1.7.25.jar
├── jsr305-1.3.9.jar
├── jul-to-slf4j-1.7.25.jar
├── log4j-over-slf4j-1.7.25.jar
├── logback-classic-1.1.11.jar
├── logback-core-1.1.11.jar
├── slf4j-api-1.7.25.jar
├── snakeyaml-1.17.jar
├── spring-aop-4.3.13.RELEASE.jar
├── spring-beans-4.3.13.RELEASE.jar
├── spring-boot-1.5.9.RELEASE.jar
├── spring-boot-autoconfigure-1.5.9.RELEASE.jar
├── spring-boot-starter-1.5.9.RELEASE.jar
├── spring-boot-starter-jdbc-1.5.9.RELEASE.jar
├── spring-boot-starter-logging-1.5.9.RELEASE.jar
├── spring-context-4.3.13.RELEASE.jar
├── spring-core-4.3.13.RELEASE.jar
├── spring-expression-4.3.13.RELEASE.jar
├── spring-jdbc-4.3.13.RELEASE.jar
├── spring-tx-4.3.13.RELEASE.jar
├── tomcat-jdbc-8.5.23.jar
└── tomcat-juli-8.5.23.jar

After that I was able to successfully run:

./flyway migrate

And was able to verify that both sql and java migrations had been successfully applied:

./flyway info

+-----------+---------+-------------------+-------------+---------------------+---------+
| Category  | Version | Description       | Type        | Installed On        | State   |
+-----------+---------+-------------------+-------------+---------------------+---------+
| Versioned | 1       | m1                | SQL         | 2018-06-09 07:41:57 | Success |
| Versioned | 2       | m2                | SQL         | 2018-06-09 07:41:57 | Success |
| Versioned | 3       | SampleJava script | SPRING_JDBC | 2018-06-09 07:47:56 | Success |
+-----------+---------+-------------------+-------------+---------------------+---------+

Phew! This is much harder work than packaging the migrations with the application, in my opinion.

One of the other downsides of this approach is that if someone adds a new java migration with an additional dependency (e.g. commons-lang3, or whatever else) that dependency needs to be added to the <RUNNER_DIR>/lib directory as well.

Hope this helps!

codemonkey
  • 3,510
  • 3
  • 23
  • 35
  • Please find jar contents [here](https://github.com/Vinni389/flyway-database-migration). And my migration scripts [here](https://github.com/Vinni389/database-migration-scripts/blob/master/README.md) – Vinni Jun 03 '18 at 00:40
  • In my case, there are two spring-boot projects. One is a flyway runner (jar). The other is to hold migration scripts (`.sql` & `.java` based). – Vinni Jun 03 '18 at 01:05
  • Updated my question. Tried placing java migration files in `src/main/java/db/migration`. No luck. – Vinni Jun 03 '18 at 01:55
  • Are you specifying the locations using "filesystem:", as per the [README](https://github.com/Vinni389/flyway-database-migration/blob/master/README.md)? If so, as per the above, that won't pick up java migrations, only sql ones. – codemonkey Jun 03 '18 at 09:35
  • Java migrations need to be on the classpath. You could: 1) add the directory to the classpath (which would be [a bit messy](https://stackoverflow.com/a/15930980/6797663) as you could no longer use `-jar`) and then 2) set the location to `classpath:db/migration`. Then both the java and sql migrations would be discovered. – codemonkey Jun 03 '18 at 09:41
  • Yes I was setting location as "filesystem:path/.." as there were only Sql scripts. After adding Java migration script, tried to send both locations (filesystem, classpath) because Flyway accepts multiple locations. No luck with classpath – Vinni Jun 03 '18 at 09:52
  • Would try with `-cp` approach. Thanks. – Vinni Jun 03 '18 at 09:54
  • As an aside, there is a standalone [Flyway Runner](https://flywaydb.org/documentation/commandline/) available, if that's an option for you. But if possible, I'd recommend packaging the sql and java migrations with your application and letting spring-boot [automatically run the migrations on startup](https://docs.spring.io/spring-boot/docs/current/reference/html/howto-database-initialization.html#howto-execute-flyway-database-migrations-on-startup). I think there's a lot of benefit in that approach. Not least that you can check out any commit and recreate the database any that point in time. – codemonkey Jun 03 '18 at 09:54
  • correct me if soemthing is missed. No luck with `java [datbaseConnectionDetailsAsOptions] -cp C:\database-migration-scripts\target\classes;.\flyway-database-migration.jar com.flywaydatabasemigration.FlywayDatabaseMigrationApplication --migrationScriptsLocation="classpath:db\migration"> RunLog.log`. Getting error saying `cannot find or load main class`. How to tell the command to get Main class from JAR file. – Vinni Jun 04 '18 at 23:35
  • What do you mean by `` in `java -cp C:\database-migration-scripts; ...` – Vinni Jun 05 '18 at 01:00
  • The main class would be `org.springframework.boot.loader.JarLauncher`. It's specified in the `META-INF/MANIFEST.MF` file of your jar. The classpath is whatever jars your java migration needs to run, which is quite a lot: spring-jdbc, flyway-core, mariadb-java-client plus all their dependencies. – codemonkey Jun 05 '18 at 18:02
  • I don't think the `-cp` approach can work with a spring boot executable jar. Have updated my answer with an alternative, which is hopefully of use. – codemonkey Jun 09 '18 at 09:56
  • @Vinni does this help? Any comments or feedback would be appreciated – codemonkey Jun 12 '18 at 05:06
  • yes it helped me to think in a way to add classpaths. But -cp approach didn't work for me. `-Dloader.path` worked for me. Also, I didn't try with your new update on 9th Jun. Would try your way and mark this as an answer. Thanks for your support. – Vinni Jun 12 '18 at 22:03
  • Now passing [multiple locations](https://github.com/Vinni389/flyway-database-migration/blob/master/src/main/java/com/flywaydatabasemigration/ApplicationExecutor.java#L73) (sql, java-based) to `flyway-database-migration`. I would really want to thank you for your support. Will try with your comments and let you know. – Vinni Jun 12 '18 at 22:09