Define multi level spring application.yml hierarchy - spring

However, the most important profiles-related feature that Spring Boot brings is profile-specific properties files. These have to be named in the format application-{profile}.properties.
Spring Boot will automatically load the properties in an application.properties file for all profiles, and the ones in profile-specific .properties files only for the specified profile.
-- from https://www.baeldung.com/spring-profiles
So this is possible:
application.yml:
spring:
datasource:
driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
application-dev.yml:
spring:
datasource:
url: jdbc:sqlserver://development.server:14331;databaseName=Dev;
When you use the dev profile, Spring would load the 'driver-class-name' from application.yml
I am searching for something like this:
application-dev-myconfig.yml:
spring:
datasource:
username: myVeryOwnDbUser
password: myVeryOwnPassword
Now if you use the dev-myconfig profile it should take the 'url' from dev profile and the 'driver-class-name' from the default profile.
Is it possible to have another level, like this? Or how would you do that?
Thanks and have a good Day :)

You can have more than one active spring profile and that would solve your problem.
In your case -Dspring.profiles.active=dev,dev-myconfig

Related

Spring boot 2.4.x cannot handle multi document yml files from config server

Java version: 8
Spring Boot version: 2.4.1
Spring Cloud version: 2020.0.0, specifically I use a Spring Cloud Config Server connected to GIT and our services are Spring Cloud Config Clients.
I have migrated away from using bootstrap.yml and started using spring.config.import and spring.config.activate.on-profile as mentioned in the documentation here and here
My configuration in my service, who is a client to the config server looks like this:
server.port: 9001
spring:
application.name: my-rest-service
config.import: configserver:http://localhost:8888
cloud.config.profile: ${spring.profiles.active}
My configuration in the config server looks like this:
application.yml (has two documents separated by the ---)
logging:
file.name: <omitted>
level:
root: INFO
---
spring:
config.activate.on-profile: dev
logging.level.root: DEBUG
my-rest-sercive.yml (has two documents separated by the ---)
spring:
datasource:
driver-class-name: <omitted>
username: <omitted>
password: <omitted>
---
spring:
config.activate.on-profile: dev
datasource.url: <omitted>
Because there is a profile "dev" active, I successfully get the following 4 configurations from config server:
application.yml: general logging level
application.yml: specific logging for dev
my-rest-sercive.yml: general datasource properties
my-rest-sercive.yml: specific datasource url for dev
I can see these 4 sources successfully being fetched when I use my browser or when I debug or in the logs when I lower the loglevel to trace:
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\my-rest-service.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\my-rest-service.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\application.yml'
o.s.b.c.config.ConfigDataEnvironment : Adding imported property source 'configserver:https://git.company.com/path.git/file:C:\configservergit\config\application.yml'
However, notice that because I use multi document yml files, out of these 4 property sources only TWO unique names are used.
In a later step, when Spring creates the data source bean, he complains he cannot find the data source URL. If I debug the spring bean factory I can indeed see that out of the 4 property files returned by the config server, only two have remained (the ones that don't contain the dev profile specific configuration). I assume this is because they have an identical name and they overwrite each other. This is an effect of this piece of code in the MutablePropertySource.class:
public void addLast(PropertySource<?> propertySource) {
synchronized(this.propertySourceList) {
this.removeIfPresent(propertySource); <-- this is the culrprit!
this.propertySourceList.add(propertySource);
}
}
This is a breaking change from Spring 2.3/Spring Cloud Hoxton where it correctly collected all properties. I think spring cloud needs to change the config server so that every document within a yml has has a unique name when returned to Spring. This is exactly how Spring Boot handles multi document yml files, by appending the String (documenyt #1) to the property source name
I found an interesting note about profiles and multi document yml, basically saying it is not supported, but this doesn't apply to my use case because my yml files are not profiles based (there is no -{profileName} in the last part of the file name).
This is a known issue with the new release. We can track the issue here on the spring cloud config server github page.
The workaround seems to be stop using multi document yml files and use multiple distinct files with the profile name in the filename.

JPA with Multiple database and application.yml

My application (spring-boot) need to access multiple databases.
For some reason, I can't find a propper example using application.yml
This example: http://smasue.github.io/spring-yml-datasources
-> application.yml but not jpa
this example: https://www.baeldung.com/spring-data-jpa-multiple-databases
-> jpa but not application.yml
So I created a very simple project based on this gs: https://spring.io/guides/gs/accessing-data-jpa/
You can find my simple example here: https://github.com/Tyvain/JpaMultipleDatabaseAndApplicationYml
spring:
datasource:
db-1:
url: jdbc:postgresql://10.10.100.100:5432/db1
username: db1
password: db1
driver-class-name: org.postgresql.Driver
db-2:
url: jdbc:postgresql://10.10.100.100:5432/db2
username: db2
password: db2
driver-class-name: org.postgresql.Driver
From here, I am not sure how to affect my repositories to each database.
This example https://www.baeldung.com/spring-data-jpa-multiple-databases is unclear as it's based on properties... and I am not sure how to adapt all code
#PropertySource({ "classpath:persistence-multiple-db.properties" })
[...]
properties.put("hibernate.hbm2ddl.auto",
env.getProperty("hibernate.hbm2ddl.auto"));
properties.put("hibernate.dialect",
env.getProperty("hibernate.dialect"));
How would you assign each repo (CustomerRepositoryDB1 and CustomerRepositoryDB2) to their database ?
Properties and yaml are two absolutely equal means of configuration. The format is only slgihtly different.
You could just replace foo.properties with foo.yml and
com.foobar.var1=value
com.foobar.var2=value2
simply becomes
com.foobar:
var1: value
var2: value2
Plus there is an official Spring Data Repository on Github full of examples. There is even one with two datasources, configured completely in code, no yaml or properties needed:
https://github.com/spring-projects/spring-data-examples/tree/master/jpa/multiple-datasources
In Application.java they exclude the AutoConfig classes and then in each package (Order, Customer), they have a Config class, configuring the datasource. Then there is no need to set the datasource on the repository itself, as that is handled by package scanning with:
factoryBean.setPackagesToScan(OrderConfig.class.getPackage().getName());
in the config. To reiterate: it's datasource per java package, no annotation on the Repository needed.

Unable to generate different spring-cloud-gateway routes based on active Spring profile

I'm trying to define different routes in Spring Cloud Gateway using Spring profiles. Everything I've read about how Spring manages profiles using yaml sems to indicate it should work, but for the life of me it just ... doesn't seem to. (My other Spring apps use properties, so I'm unused to the yaml config -- it's possible I'm missing something.)
Effectively what I'm trying to do is to have a "prod" profile which contains the URIs for my production servers, and a "dev" profile which contains the localhost equivalents.
I have two profiles, dev and prod. My application.yml file looks like this
spring:
profiles:
default: prod
management:
endpoint:
health:
enabled: true
endpoints:
web:
exposure:
include: hystrix.stream, gateway
---
spring
profiles: prod
cloud:
gateway:
routes:
- id: test_route
uri: http://foo.mycompany.com
predicates:
- Path=/status
- Method=GET
---
spring
profiles: dev
cloud:
gateway:
routes:
- id: test_route
uri: http://localhost:8080
predicates:
- Path=/status
- Method=GET
My understanding is as follows:
the spring.profiles.default property tells Spring that, if no profile is specified, to use the prod profile
Spring will treat the --- as a "file separator" and re-evaluate each set of properties and overwrite previous values if the spring.profiles parameter evaluates true
Given this understanding, I would expect Spring to parse the "default" properties first, learning that the default activated profile should be prod. Then it will parse the prod properties. Since "prod" is an active profile (the only active profile, being the default), it should parse and apply the prod routes. Then it would parse the dev routes, but recognize that dev is not an active profile, and not overwrite those values. This is my understanding from reading the documentation on how to change config based on the environment.
However, when I load this, and I hit the actuator endpoint -- /actuator/gateway/routes -- I get back [] where I would expect to see the prod routes. I do see in my logs that the prod profile is activated, but it seems like not having the properties in the "default" section at top causes them to not be applied when the parser reads them out of the profile section.
The other thing I tried was putting the "dev" properties as the defaults, and then attempting to use the "prod" profile properties to overwrite the URIs. A similar issue happened there -- I hit the actuator endpoint and got back routes, but they were just the dev ones from the default.
How can I leverage Spring profiles to configure different Spring Cloud Config routes in my application.yml ?
Versions:
spring-cloud-gateway 2.0.1.BUILD-SNAPSHOT (to get a workaround for this bug, probably not relevant)
spring-cloud-starter-gateway
spring-boot 2.0.3.RELEASE
spring-boot-starter-webflux
spring-boot-starter-actuator
(I can't use Spring Cloud Config for political reasons. My company's chief architect has a severe case of Not Invented Here Syndrome.)
You cannot use spring.profiles.default in the property file. It will be too late for setting such value.
So you can set it using program argument (or System property). E.g.
java -jar --spring.profiles.default=dev your-app.jar
Or you can do it in the code by hardcoding the default profile:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
ConfigurableEnvironment environment = new StandardEnvironment();
environment.setDefaultProfiles("dev");
application.setEnvironment(environment);
application.run(args);
}
}
Some related information you can find here by reading all comments:
https://github.com/spring-projects/spring-boot/issues/1219

Spring Boot: failed to load JNDI variables into yml configuration file

According to Spring Boot documentation about Externalized configuration, I tried to load a JNDI variable into my yml configuration file, like this:
spring:
# Show or not log for each sql query
jpa:
show-sql: java:global/bc-api-immop/hibernate/show_sql
And it doesn't work.
I have my variable in my JNDI context:
I also tried this:
spring:
# Show or not log for each sql query
jpa:
show-sql
jndi-name: java:global/bc-api-immop/hibernate/show_sql
But, still the same result.
Do you have any idea what I'm doing wrong?
As weird as it sounds, I also have this code, and it works:
spring:
# Set here configurations for the database connection
datasource:
jndi-name: java:jboss/datasources/bc-appli-as400-ds
Edit: When I do this, it works to (so my issue really comes from getting my JNDI variable):
spring:
# Show or not log for each sql query
jpa:
show-sql: true
The way you have written your yaml is the real problem here
The general concept is key: value so by writing show-sql spring boot expects a value of true or false as it appears on Appendix A. Common application properties so it is normal that your property configuration fails and I am pretty sure that this is showing up somewhere in your log files.
On the first example when you write show-sql: java:global/bc-api-immop/hibernate/show_sql you are actually answering the question "Should I show you the generated SQL statements" with "Hi spring Boot this is your datasource" which we both understand it makes no sence to spring boot :)
Your last statement, on the other hand, is correct. You are defining under the yaml collection item datasource the property jndi-name: with value java:jboss/datasources/bc-appli-as400-ds
I would also suggest spending 10 minutes to read this article Learn X in Y minutes
So a correct approach would probably be
spring:
# Set here configurations for the database connection
datasource:
jndi-name: java:jboss/datasources/bc-appli-as400-ds
jpa:
show-sql: true
Let me know if I can help you more

How to config mybatis mapper xml files in Spring boot's application.yml

I am a newbie to spring boot and I want to make my spring boot application integrate with mybatis and mysql database.
I generate the entity,mapper interfaces,and the mapper xml files with the generator. And I also use #MapperScan annotation to tell the application how to find the mappers, my application.yml looks like following:
spring:
datasource:
username: root
password:
url: jdbc:mysql://localhost:3306/bidding
driver-class-name: com.mysql.jdbc.Driver
mybatis:
mapperLocations: classpath:mapping/*.xml
But when I run the application with command 'mvn spring-boot:run', it always give me the error message 'Invalid bound statement (not found)'.
Is there anything I missed in my code?
Something wrong with the mapping of mybatis interface and xml.
check package of interface and xml ,they should be the same package
check the namespace in xml and package of xml,they should be same path

Resources