How to set Flyway migration file location using Spring profiles - spring

I have two Spring profiles dev and test configured for development and test environment. And in each environment I am using different databases viz h2 in dev and postgresql in testing. Following are my properties files for each profile where {vendor} is resolved by spring boot to h2 and postgresql repectively as per datasource configured.
application-dev.properties
spring.flyway.locations=classpath:db/migration/{vendor}
application-test.properties
#Data source
spring.datasource.url=jdbc:postgresql://localhost:5432/test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
#Flyway
spring.flyway.check-location=false
spring.flyway.locations=classpath:/db/migration/test/{vendor}
Flyway migration files for dev profile are under test/resources and for test profile under main/resources
This is working fine when I run my application with test profile where it picks migration files only under main/resources. However, When I run my unit test using dev profile. I expect it to pick files only under src/test/resources/db/migration/h2. But Flyway is picking up migration files from main/resources and test/resources both leading to error
org.flywaydb.core.api.FlywayException: Found more than one migration with version 1
I don't understand this behavior. Any inputs on how to fix this?

So, here is how I did it.
Requirements:
Use Spring profiles to configure application for different environments viz dev, test and prod.
Use Spring profiles to load flyway migration files as per the environment.
Database per environement:
H2 database for dev environment.
postgresql database for test environment.
postgresql database for prod environment.
Configuration
Create Spring profiles dev, test and prod in pom.xml.
<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>test</id>
</profile>
<profile>
<id>prod</id>
</profile>
</profiles>
Create properties files for each profile
application-dev.properties
spring.flyway.locations=classpath:db/migration/{vendor}
Since, H2 database is configured by Spring boot when H2 driver is on classpath. We don't need to configure it explicitly.
application-test.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/db_test
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.flyway.locations=/db/{vendor}/common,/db/{vendor}/test
application-prod.properties
spring.datasource.url=jdbc:postgresql://localhost:5432/db_prod
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.flyway.locations=/db/{vendor}/common,/db/{vendor}/prod
Flyway migration file locations.
If you will notice, I have not used db/migration under src/main/resources to place migration files which is the default location. For simple reason that Flyway picks all the files under this location and which results in version conflict between files for different environments. For e.g V2__data_insertion.sql is present for all three environments and this will not work if these were nested under db/migration. Since, H2 migration files pertain to default profile I have left them at the default flyway migration file location.
Hope that helps !!!

Its not about the flyway only.
In maven there are two different classpathes that it works with during the build:
Compile classpath - used for compilation (including src/main/*)
Testing classpath - effectively including both src/test/* (obviously) and src/main/* because in tests you should have a compile time access to the actual code.
That's why in runtime there are effectively 2 accessible places and flyway finds more than one migration.
Another observation:
Your production code in general should not include anything about tests. But I see that you add: src/main/resources/application-test.properties This file will appear in the production artifact which is wrong.
As a workaround you can work with
src/main/resources/application-prod.properties that defines location "X" for real migrations
As opposed to:
src/test/resources/application-test.properties that defines location "Y" for test migrations
Run integration tests with Profile Test and you won't find production migrations.

Related

Spring web application with profile

I have a spring application with multiple profile like dev,prod. application running on external tomcat.profile are setting in the pom.xml file
pom.xml
<profiles>
<profile>
<id>dev</id>
<properties>
<repoManager>Manager1</repoManager>
..........
</properties>
</profile>
</profiles>
Project have applicationContext.xml file.
One of the bean definition, we are taking repoManager the value from dev profile.
when i run the application, throwing the following error
rg.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name '<Class name>' defined in class path resource [applicationContext.xml]: Circular placeholder reference 'repoManager' in property definitions
How to run spring web application with profile dev
I have done following changes
Added spring.profiles.active=dev in catalina.properties file
Added setenv.sh in tomcat bin folder
Added spring.profiles.active=dev on STS run configuration
Above 3 solution are not working.
How to solve this problem.
Thanks in advance.
I Got the answer
Right click on the project -> Maven -> Select Maven Profiles... and pickup your profile.
No Need to do above steps.

How to externalize application.yml in spring boot

Hi Currently my project is using application.yml from src/main/resources by default. I want to use the application.yml file in a different location, so as I can edit the properties whenever I want. Please suggest any idea
Spring Boot lets you externalize your configuration so that you can work with the same application code in different environments. You can use properties files, YAML files, environment variables, and command-line arguments to externalize configuration. Property values can be injected directly into your beans by using the #Value annotation, accessed through Spring’s Environment abstraction, or be bound to structured objects through #ConfigurationProperties.
Detailed Information: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-yaml
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
Set JAVA_OPTS environment variable example
-Dspring.profiles.active=dev -Dspring.config.location=file:C:/application-external.yml
This will allow you to have provide multiple profiles inside of a YML file and let spring do the heavy lifting of evaluating the correct properties:
spring:
profiles: dev
someproperty: devproperty
---
spring:
profiles: test
someproperty: testproperty
To use external configuration files in your Maven build : configure the maven surefire plugin like this in your pom.xml:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dspring.config.location=file:${home}/conf/application-external.yml
</configuration>
</plugin>

Spring boot maven build executed my database operations

I'm setting up a standalone server..to modify data in the database.
When i finished, i runned maven build and the application started to modify the datas in the database. Really it just started to insert, and update everithing.
# PostgreSQL DB - "bar"
spring.datasource.jdbc-url=jdbc:postgresql://localhost:5600/bar
spring.datasource.username=
spring.datasource.password=
spring.datasource.driver-class-name=org.postgresql.Driver
# MySQL DB - "foo"
rm.datasource.jdbc-url=jdbc:mysql://localhost:5601/foo
rm.datasource.username=
rm.datasource.password=
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
That's all i've got in my application properties. I have two config classes as database configurations..and they are contains some more properties:
MySQL
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect", "org.hibernate.dialect.MySQL5Dialect");
properties.put("rm.datasource.driver-class-name", "com.mysql.jdbc.Driver");
properties.put("zeroDateTimeBehavior", "convertToNull");
Postgres
properties.put("hibernate.hbm2ddl.auto", "none");
properties.put("hibernate.dialect","org.hibernate.dialect.PostgreSQL9Dialect");
When i builded my project my application inserted in the postgreSql database.
Can i just solve it like if i'm building the project..DO NOT insert/update/delete whatever.. in my database?
If your Maven project is generated by either STS (Spring Tool Suite) or Spring Initializr, they are going to automatically create unit test class as follows:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ApplicationTest {
#Test
public void contextLoads() throws Exception {
}
}
The #SpringBootTest annotation tells Spring Boot to go and look for a main configuration class (one with #SpringBootApplication for instance), and use that to start a Spring application context.
Therefore, if you want to skip unit tests temporally while running Maven build, there are several ways:
For Eclipse, check the Skip tests checkbox.
Via command line, executing mvn install -DskipTests or mvn install -Dmaven.test.skip=true.
Set the skipTests property to true of <groupId>org.apache.maven.plugins</groupId> in your pom.xml.
BTW, you should also check why your application will perform inserting/updating data to database when starts.
Hi I see different possible solutions:
Skip test with mvn clean install -DskipTests or mvn clean install -Dmaven.test.skip=true
Remove all the casses from src/test/**
In case you will need to proceed with tests in the future, define an in memory database for the test adding:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
to your pom.xml
For the last solution you may need some initialisation scripts for the DB

Setting application-specific properties on deploy by maven

I would like to deploy java app twice to one tomcat server, each time with different environment properties.
I would like to find a way like
mvn tomcat7:deploy -Denvironment=local
I don't mind using other maven plugin.
With no need to change files after deploy.
Is it somehow possible?
Thank you for you answer.
You can have a configuration file (something like e.g. application.properties) that you enable resource filtering for. You could then parameterize the values in that configuration file, and pass in different parameter values for each of the deployments (-Dkey1=value1 -Dkey2=value2).
You could pass the different parameter values on the command-line, or you could cement them in different profiles, and just activate the appropriate profile from the command-line (-Psecond-deployment).
What seems a little unfortunate from the suggested approach in your question is: in its most basic sense Maven is a build tool, meant to produce a consistent artifact from a build. This is no longer the case with the suggested approach in your question. If this is a web application that is never used as a dependency of other module, this may be fine. But just highlighting that different deployment configurations is a deployment concern, not a build concern.
I used little workaround.
I created different profiles which I use to update application property.
example:
In my POM I have:
<profiles>
<profile>
<id>local</id>
<properties>
<environment>local</environment>
....
</properties>
</profile>
and in application.property file I have
environment=#environment#
(That # are correct! )
This way I can deploy my app with defined environment by using command
mvn -P local tomcat7:deploy
and use environment variable anywhere else as ${environment}

Could i use maven profile to swith between different value application.properties

I have application-prod.yml application-dev.yml, and application.properties which containing just one line code like below
spring.profiles.active=dev
for maven production build, it should use spring.profiles.active=prod , then it will build with application-prod.yml, for development build, it should use spring.profiles.active=dev, then maven
will use application-dev.yml to build
could I use pom.xml's different profile to do switch for this value switch in applicaiton.properties?
You can use a Maven property for this, reference it in your yml file (with ${...}) and filter the resource (i.e. the yml file) with the maven resources plugin.
It seems that what you're after is "externalized configuration". According to the excellent 12factor guidelines, it is best not to keep such config inside your code-repository.
Refer to the relevant section in the Spring Boot manual to see which options you have (and there are many). What it comes down to is that you provide your application.yml/properties file on the filesystem and your application will read it from there, rather than from the classpath.
Also, note that spring-profiles are not meant to be used to distinguish between development environments, but rather to put the application in different functional modes (e.g. to enable or disable specific features).
If you want the content of your properties file changed at build time, then you can use Maven filtering. Maven filtering allows to replace a placeholder in your properties (or yaml) file by values from Maven properties.
Assuming you have a property in your POM called targetEnv, which might have either the value dev or prod (depending on the active Maven profile), then you can refer it in your properties file (or yaml file) by using the following syntax :
spring.profiles.active=#targetEnv#
However, if you want to follow Spring Boot recommandations, it is better to enable and disable the Spring profiles by the means of environment variables in your target environment. For instance, you can use an environment variable spring.profiles.active with the desired value and it will override the value in your properties file.
You need to define a custom property in each of your Maven profiles and set their values to match with suffixes of corresponding properties files that you want to load with a particular profile.
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>release</id>
<properties>
<activatedProperties>release</activatedProperties>
</properties>
</profile>
Next, in the build section of the same file, configure filtering for the Resources Plugin. That will allow you to insert properties defined in the previous step into any file in the resources directory, which is the subsequent step.
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
…
</build>
Finally, add the following line to the application.properties.
spring.profiles.active=#activatedProperties#
For more details, please see spring boot properties per maven profile
For official guide to load from external configLoad from external Config

Resources