Flyway Init Order - java

I've got a Java Spring Boot application, with Flyway configured as a dependency in my Maven pom.xml (I have a parent pom and a project pom... Flyway is defined in my project pom).
<dependencies>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>4.2.0</version>
</dependency>
...
and just a couple of entries in application.properties:
flyway.enabled=true
flyway.out-of-order=true
I can run a maven task to get Flyway to run migrate to create/update my database and then run my application against that, but I'm having trouble getting it to call migrate at the right time just by running my application (which is obviously important in prod). It looks like all of my Spring classes are being instantiated first, some of which involves looking at the database, and then Flyway migration happens after, so for instance if you run the application against an empty database the application crashes when trying to access anything in the database.
Any tips on where to look to see where I'm going wrong to get Flyway to do its migration earlier in the start-up process of my Spring Boot application?

I am not sure how your data source configuration looks like, but you could declare your JPA config in such a way that makes it dependent on the flyway migration.
You could declare a #DependsOn("flyway") annotation on whatever #Config class or datasource bean, "flyway" being the declared name of your flyway configuration bean. Then, on your flyway configuration bean, qualify the bean annotation with an initMethod property like the following: #Bean(initMethod = "migrate").

Try change "flyway.out-of-order=false"

I would suggest you to try using event listners like ApplicationStartedEvent.
#EventListener
public void migrate(ApplicationStartedEvent applicationStartedEvent) {
//do some checks
flyway.migrate();
}

Related

Can't run my Quarkus app after adding JPA

I'm trying to learn Quarkus, but after adding a JPA dependency the app doesn't initialize anymore.
This is the added dependency:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>
Following are the errors I'm having:
[org.tes.uti.TestcontainersConfiguration] (build-47) Attempted to read Testcontainers configuration file at file:/home/fhb/.testcontainers.properties but the file was not found. Exception message: FileNotFoundException: /home/fhb/.testcontainers.properties (No such file or directory)
After that Quarkus keeps on and gets the following error:
Caused by: java.lang.RuntimeException: io.quarkus.runtime.configuration.ConfigurationException: Model classes are defined for the default persistence unit <default> but configured datasource <default> not found: the default EntityManagerFactory will not be created. To solve this, configure the default datasource. Refer to https://quarkus.io/guides/datasource for guidance.
This is my application.properties file:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=postgres
quarkus.datasource.password=admin
quarkus.datasource..jdbc.url=jdbc:postgresql://localhost:5432/quarkus-social
quarkus.datasource.jdbc.max-size=16
I think that Quarkus is trying to run tests and for that it needs the .testcontainers.properties file, which I've never created. Anyways I don't want to create that file in /home/fhb/, so theres a way to specify that file location?
Besides thatI would like to know if Testcontainers has something to do with unit tests, which I would like to add to my quarkus application.
Thanks in advance for your help.
I guess the problem is a small typo.
Change from
quarkus.datasource..jdbc.url=jdbc:postgresql://localhost:5432/quarkus-social
To
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/quarkus-social
If you don't specify the URL of the database and run in dev or test mode, Quarkus uses test containers to start one database for you.
There are tutorials on quarkus.io/guides/datasource.
About tests, you can use test containers or one in memory database as H2. You can find all this on Quarkus guides.

SpringBoot 2 with Flyway: spring.flyway.locations is ignored

I am using Spring Boot 2.2.2 with Flyway 5.2.4 and I tried to configure flyway to use a differente location for the scripts, but spring.flyway.locations=filesystem:db_other/migration/{vendor} neither flyway.locations=filesystem:db_other/migration/{vendor} configurations on application.properties worked.
When running the program, the following exception appear in the log:
FlywayMigrationScriptMissingException: Cannot find migration scripts in: [classpath:db/migration]
I already tried using Spring Boot 2.2.1, 2.2.0, 2.1.11 and Flyway 6.1.0 and 6.1.3, but the result is the same.
The default value for that property is classpath:db/migration as shown here (search for flyway).
Since you're using a different folder in the resources directory you should only need to change "filesystem" to "classpath" in your application.properties value.
Actually if was my fault: as I used to work with just spring (not spring boot) I configured my test class with the annotations #ExtendWith(SpringExtension.class) AND #ContextConfiguration(classes = { MyConfiguration.class }) instead of just use #SpringBootTest. When making this change the test worked.

Global variables And Application Variables Defining in Spring boot project

I am trying to develop micro service by using spring and spring boot. In my project , I am converting monolithic to service oriented architecture. Project contain 20 Micro services.I these I need to set application variables and global variables. I have confusions related to this , And I am adding those confusions here,
Is possible to declare my global variables in application.properties file? If not possible where I can define my global variables?
If I am using spring config server for global configuration, How I can import those properties conditionally to client project?
Can I set different property files for different profiles in config server , and conditionally import into client project for different profiles? Here each profile representing different regions in my case.
After My Exploration I find out Solution for this problem for loading global variables and application variables including database configuration. The best way we can use that is - spring cloud config server externalized configuration.
We can create a microservice for spring cloud config server. In config server we can create our variables and configuration in two ways.
Configurations from GIT Link reference
Using local file system / Environment variables.
Links To refer
https://cloud.spring.io/spring-cloud-config/single/spring-cloud-config.html
https://cloud.spring.io/spring-cloud-static/spring-cloud-config/1.3.3.RELEASE/multi/multi__spring_cloud_config_server.html
https://github.com/spring-cloud/spring-cloud-config
Here I followed using local file system.
Need to create Config folder under src/main/resources. And create different profiles by following naming convention,
db,properties , db-test.properties , db-prod.properties , db-dev.properties.
I created for example for different development environment. Like we can create any profiles for variables and configuration.
And add following in application.properties for config server
server.port=8888
spring.profiles.active=native
Add config server dependency in pom.xml file of config server,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
Add the following into main application run class,
#SpringBootApplication
#EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
And also create client microservice project by adding pom.xml dependency,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Add following line in application.properties file for setting client to receive configuration from server,
server.port=8080
spring.application.name=db
spring.cloud.config.uri=localhost:8888
Finally run your client project by specifying profile ,
java -jar -Dsping.profiles.active=<profile> <jar_name>.jar
Thanks In advance

Why Spring Boot 2.0 application does not run schema.sql?

While I was using Spring Boot 1.5, on application startup Hibernate executed schema.sql file located in /resources folder when appropriate configuration is set. After Spring Boot 2.0 release this feature does not work any more. I couldn't find anything about this change in documentation.
Here is my application.properties file content:
spring.datasource.url=...
spring.datasource.username=...
spring.datasource.password=...
#spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
Is there some change in Spring Boot 2.0 or is this an bug/issue?
Check the documents here.
In a JPA-based app, you can choose to let Hibernate create the schema
or use schema.sql, but you cannot do both. Make sure to disable
spring.jpa.hibernate.ddl-auto if you use schema.sql.
You have spring.jpa.hibernate.ddl-auto=create-drop that's why schema.sql is not executed.
Looks like this is the way Spring Boot works.
Edit
I think that the problem(not really a problem) is that your application points to a mysql instance.
See the current Spring Boot properties:
spring.datasource.initialization-mode=embedded # Initialize the datasource with available DDL and DML scripts.
The default value is embedded - e.g. initialize only if you're running and embedded database, like H2.
Also see the answer of Stephan here. He said:
Adding spring.datasource.initialization-mode=always to your project is
enough.
So try to set:
spring.datasource.initialization-mode=always
Not embedded (e.g. MySQL)
If you load a database that is not embedded, in Spring Boot 2 you need to add:
spring.datasource.initialization-mode=always
Check the Migration Guide:
Database Initialization
Basic DataSource initialization is now only enabled for embedded data
sources and will switch off as soon as you’re using a production
database. The new spring.datasource.initialization-mode (replacing
spring.datasource.initialize) offers more control.
Embedded (e.g. h2)
I once had a similar problem, even though it was an h2 (so it was an embedded DB), my h2 configuration was activated by a my-test profile.
My test class was like:
#RunWith(SpringRunner.class)
#SpringBootTest // does not work alone
#ActiveProfiles("my-test")
public class MyEntityRepositoryTest {
The problem is #SpringBootTest alone did not initialize the test database. I had to either use #DataJpaTest or #SpringBootTest+#AutoConfigureTestDatabase. Examples
#RunWith(SpringRunner.class)
#DataJpaTest // works
#ActiveProfiles("sep-test")
public class MyEntityRepositoryTest {
or
#RunWith(SpringRunner.class)
#SpringBootTest // these two
#AutoConfigureTestDatabase // together work
#ActiveProfiles("sep-test")
public class MyEntityRepositoryTest {
Recent Update
As of Spring Boot Version 2.7
the property spring.datasource.initialization-mode has been removed.
You should from this version and onwards use the replacement property spring.sql.init.mode
Example: spring.sql.init.mode:always
Spring Boot 2.7 changelog
It works fine for me, you can try it. Set datasource type to what you like instead of HikariCP.
spring.datasource.initialization-mode=always
spring.datasource.type=com.mysql.jdbc.jdbc2.optional.MysqlDataSource
spring.jpa.hibernate.ddl-auto=none
There have another problem may result data.sql can not be executed,when you don't config the spring.jpa.hibernate.ddl-auto=none,that the data.sql will not be execute d.
I was able to make application run only after excluding Hikary CP like that:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<exclusions>
<exclusion>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
</exclusion>
</exclusions>
</dependency>
Please, see the issue here

Spring integration tests with profile

In our Spring web applications, we use the Spring bean profiles to differentiate three scenarios: development, integration, and production. We use them to connect to different databases or set other constants.
Using Spring bean profiles works very well for the changing the web app environment.
The problem we have is when our integration test code needs change for the environment. In these cases, the integration test loads the application context of the web app. This way we don't have to redefine database connections, constants, etc. (applying the DRY principle).
We setup our integration tests like the following.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
public class MyTestIT
{
#Autowired
#Qualifier("myRemoteURL") // a value from the web-app's applicationContext.xml
private String remoteURL;
...
}
I can make it run locally using #ActiveProfiles, but this is hard-coded and causes our tests to fail on the build server.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = ["classpath:applicationContext.xml"])
#ActiveProfiles("development")
public class MyTestIT
{ ... }
I also tried using the #WebAppConfiguration hoping that it might somehow import the spring.profiles.active property from Maven, but that does not work.
One other note, we also need to configure our code so that developers can run the web app and then run the tests using IntelliJ's test runner (or another IDE). This is much easier for debugging integration tests.
As other people have already pointed out, you can opt to use Maven to set the spring.profiles.active system property, making sure not to use #ActiveProfiles, but that's not convenient for tests run within the IDE.
For a programmatic means to set the active profiles, you have a few options.
Spring 3.1: write a custom ContextLoader that prepares the context by setting active profiles in the context's Environment.
Spring 3.2: a custom ContextLoader remains an option, but a better choice is to implement an ApplicationContextInitializer and configure it via the initializers attribute of #ContextConfiguration. Your custom initializer can configure the Environment by programmatically setting the active profiles.
Spring 4.0: the aforementioned options still exist; however, as of Spring Framework 4.0 there is a new dedicated ActiveProfilesResolver API exactly for this purpose: to programmatically determine the set of active profiles to use in a test. An ActiveProfilesResolver can be registered via the resolver attribute of #ActiveProfiles.
Regards,
Sam (author of the Spring TestContext Framework)
I had a similar problem: I wanted to run all of my integration tests with a default profile, but allow a user to override with a profile that represented a different environment or even db flavor without having to change the #ActiveProfiles value. This is doable if you are using Spring 4.1+ with a custom ActiveProfilesResolver.
This example resolver looks for a System Property, spring.profiles.active, and if it does not exist it will delegate to the default resolver which simply uses the #ActiveProfiles annotation.
public class SystemPropertyActiveProfileResolver implements ActiveProfilesResolver {
private final DefaultActiveProfilesResolver defaultActiveProfilesResolver = new DefaultActiveProfilesResolver();
#Override
public String[] resolve(Class<?> testClass) {
if(System.getProperties().containsKey("spring.profiles.active")) {
final String profiles = System.getProperty("spring.profiles.active");
return profiles.split("\\s*,\\s*");
} else {
return defaultActiveProfilesResolver.resolve(testClass);
}
}
}
And in your test classes, you would use it like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles( profiles={"h2","xyz"},
resolver=SystemPropertyActiveProfileResolver.class)
public class MyTest { }
You can of course use other methods besides checking for the existence of a System Property to set the active profiles. Hope this helps somebody.
If you want to avoid hard-coding the profile you may want to use the system property spring.profiles.active and set it to whatever you need in that particular environment e.g. we have "dev", "stage" and "prod" profiles for our different environments; also we have a "test", "test-local" and "test-server" profiles for our testing.
Remember that you can have more than one profile in that system property by using a list of comma-separated values e.g. "test,test-qa".
You can specify system properties in a maven project in the maven surefire plugin or passing them like this:
mvn -DargLine="-DpropertyName=propertyValue"
As #ElderMael mentioned you could use the argLine property of maven surefire plugin. Often when I need to run all the test with different specific Spring profiles I define additional maven profile. Example:
<profiles>
<profile>
<id>foo</id>
<dependencies>
<!-- additional dependencies if needed, i.e. database drivers ->
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.profiles.active=foo</argLine>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
With that approach you could easily run all the test with activated profile by maven command:
mvn clean test -Pfoo
The #ActiveProfile annotation is good but sometimes we need to run all the test with activated specific profiles and with hard-coded #ActiveProfile parameters it is a problem.
For example: by default integration test with H2 in-memory db, but sometimes you want to run test on the "real" database. You could define that additional maven profile and define Jenkins job. With SpringBoot you could also put additional properties to test/resources with name application-foo.yml (or properties) and those properties will be taken into account to.
there are many faces to this problem.
in my case, a simple addition to build.gradle already helped:
test { systemProperties = System.properties }

Categories

Resources