I am trying to write a simple Hibernate application and I want the SQLite database location to be changed according to an application configuration file.
What I was trying to do was therefore retrieving the path of the database from a text file in the project and putting it inside dbLocation, then running the following piece of code:
Configuration config = new Configuration();
config.setProperty("hibernate.connection.url", "jdbc:sqlite:" + dbLocation);
Is there a better and more "standard" way to do so? I'm using Spring Boot for my application and I just realized there's afile called application.properties. May I use this one maybe? I'm pretty new to both Hibernate and the Spring framework.
In Spring Boot application properties you can externalize your application properties so they are configured/managed outside your application source code.
Once a property is defined in application.properties you can use SpringBoot built-in feature to access the values
#Configuration
public class ApplicationProperty {
#Value("${prop}")
private String prop;
With Spring Boot another way is to use #ConfigurationProperties.
Example from the documentation can be found here
A comparison between #ConfigurationProperties and #Value annotation from the documentation
Feature #ConfigurationProperties #Value
Relaxed binding Yes No
Meta-data support Yes No
SpEL evaluation No Yes
Related
I'm new to spring and I'm studying it. And stumbled upon the #Profile annotation.
I want to write a simple project with Spring (not Springboot) to learn how to load properties based on the environment using #profile annotation. Almost everywhere, the examples (Ex1, Ex2) I see only with the Springboot. I'm wondering whether we cannot write a Spring application that can dynamically load the properties based on the environment (dev, prod).
Some examples ( Ex3, Ex4, Ex5) show with the #Profile but those have hardcoded the bean details for each environment like below. Is this how we have to write the property loading?
#Profile("dev")
#Bean
public String devDBCcnnection() {
System.out.println(dbConfiguration.getUrl());
return "DB Connection for Dev";
}
#Profile("test")
#Bean
public String devTestCcnnection() {
System.out.println(dbConfiguration.getDriverClassName());
return "DB Connection for Test";
}
#Profile("prod")
#Bean
public String devProdCcnnection() {
System.out.println("DB Connection for Prod");
return "DB Connection for Prod";
}
It has to write a bean for each profile like in the above example?
Can someone tell me using #Profiles, can't dynamically load the property values like in Spring applications?
Appreciate it if you can give the samples with Spring 5
Almost everywhere, the examples (Ex1, Ex2) I see only with the
Springboot. I'm wondering whether we cannot write a Spring application
that can dynamically load the properties based on the environment
(dev, prod).
Spring boot uses the spring context. The spring context allows you to use profiles. Therefore no problem using profiles with simple Spring project (non spring-boot).
There are many ways that you can use Profiles.
One of them is the example that you gave with specific beans that have #Profile and get registered in spring for a specific profile.
Another one, more commonly used in enteprise applications is to ship a jar application with multiple application.yaml files. So for example you ship your application, containing dev-application.yaml and qa-application.yaml. You can then start your application selecting a specific profile to be active. Then that specific application.yaml will be used when the application starts up to build the spring context. So the aplication will be started with qa-application.yaml and will have a connection to the QA database.
But be careful the default application.yaml will also be loaded. The specific application.yaml for example qa-application.yaml will be loaded on top of default application.yaml.
The following article contains very good information about spring profiles
spring profiles article
Considering my example here, I quotte something relevant from that article.
The Default Profile The default profile is always active. Spring Boot
loads all properties in application.yml into the default profile. We
could rename the configuration file to application-default.yml and it
would work the same.
Other profiles will always be evaluated on top of the default profile.
This means that if a property is defined in the default profile, but
not in the qa profile, the property value will be populated from the
default profile. This is very handy for defining default values that
are valid across all profiles.
In order to activate a specific profile
For non spring-boot projects here is a very good answer spring active profile
For spring-boot projects you can
Use a system variable to start your jar file
java -Dspring.profiles.active=qa -jar myApp.jar
Use an environment property to start your jar file
export SPRING_PROFILES_ACTIVE=qa
java -jar myApp.jar
I'm currently using an EnvironmentPostProcessor to add my external PropertySource, the code looks like this :
public class ExternalPropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor
{
private static final String EXTERNAL_PROPERTY_SOURCE_NAME = "ExternalPropertySource";
#Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application)
{
if (environment.acceptsProfiles(Profiles.EXTERNAL_PROPERTY_SOURCE_ENABLED_PROFILE)) {
environment.getPropertySources()
.addLast(new ExternalPropertySource(EXTERNAL_PROPERTY_SOURCE_NAME, new ExternalSource()));
}
}
}
A spring.factories is also used to register this EnvironmentPostProcessor.
This code actually works if the profile is set in the vm environment variables, but if it is added in src/main/resources/application.yml, the profile doesn't seem to be injected yet in the environment and is not returned by the environment.getActiveProfiles(). I've tried using the interface Ordered with the lowest precedence, but it doesn't help.
To add a bit of context around what I'm trying to achieve, this class is in a small library that adds an external property source like a database. Then we can use it in some other spring boot applications. Something like Spring Cloud Config does.
I'd like a clean way to enable or disable this property source depending on the environment where the code runs. I don't mind using something else then profiles or even another way to inject the property source, I just want something clean that doesn't depend on several factors to work.
The main problem in my code right now is that I'm using spring boot's property sources to make my own property source configurable.
UPDATE : I used a Spring Cloud app to debug this, and was confusing the bootstrap context with the normal spring boot context. See my answer below.
Further investigation made me figure out the problem appeared only with a Spring Cloud application.
In fact the breakpoint I had in this code was triggered twice, once after the bootstrap context initialization and once after the spring boot context initialization. I was only debugging the profiles in the first occurence. At that point, only the bootstrap.yml properties are loaded and not the ones from the application.yml file. The second occurence obviously had the profiles from my application.yml file.
My code worked as expected with a vanilla Spring Boot application. As the documentation states :
The Environment has already been prepared with all the usual property
sources that Spring Boot loads by default.
I was confused by the behaviour of my app which seemed to be different from that statement, but it was Spring Cloud's bootstrap that was messing with my debugging.
Since I need a PropertySource that has the highest precedence, I need to add it in the post bootstrap initialization for Spring Cloud apps. I used an init flag on my EnvironmentPostProcessor so it doesn't get executed twice and used the bootstrap.yml on Spring Cloud apps to set the profile.
TL;DR :
With Spring Cloud, an EnvironmentPostProcessor gets called twice: once after the bootstrap init and once after the normal Spring Boot context init. If you need injected properties and are targeting the Spring Cloud's post bootstrap initialization, use the bootstrap.yml instead of application.yml.
I want to add a custom property to the application.yml of my Cloud Config. The comments in the file say it is for all shared configuration. However, when I do so, it causes the binding of the properties to JHipsters own ApplicationProperties to fail at the class does not have the correct writable property.
application.yml
application:
clients:
- Foo
Stacktrace:
Caused by: org.springframework.boot.bind.RelaxedBindingNotWritablePropertyException: Failed to bind 'application.clients[0]' from 'file:central-config/localhost-config/application.yml' to 'clients[0]' property on 'io.github.jhipster.registry.config.ApplicationProperties'
From JHipster's documentation:
Application-specific properties Your generated application can also
have its own Spring Boot properties. This is highly recommended, as it
allows type-safe configuration of the application, as well as
auto-completion and documentation within an IDE.
JHipster has generated a ApplicationProperties class in the config
package, which is already preconfigured, and it is already documented
at the bottom the application.yml, application-dev.yml and
application-prod.yml files. All you need to do is code your own
specific properties. (emphasis mine)
Have you done that step and added your own properties to ApplicationProperties.java? It looks like you should have a property of type List<String> clients. If you haven't that's why it's failing, because it's attempting to bind a configuration property to the ApplicationProperties class, but the class doesn't contain a property to store it.
Custom Spring boot properties should always be defined in a #ConfigurationProperties class within the app so that their value can be setup in the yml file. This is thoroughly documented in the spring boot documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
I was trying to understand if I have some properties in application.yml and a few in application.properties will my application read from both these files?
According to Spring documentation - Change the Location of External Properties of an Application:
No matter what you set in the environment, Spring Boot always loads application.properties as described above. By default, if YAML is used, then files with the ‘.yml’ extension are also added to the list.
In which order properties are considered is explained in the chapter Spring documentation - Externalized Configuration.
If in doubt what files have been loaded I recommend to set the log level to DEBUG which shows the loaded configuration files in the log.
There is a good article here, that describes how both of these can be read using the #ConfigurationProperties annotation.
#ConfigurationProperties supports both .properties and .yml files.
#ConfigurationProperties support JSR-303 bean validation – javax.validation
Hope this helps!
I have a spring boot application and I want to use both a yml file for my application properties and also a plain application-${profile}.properties file set to configure my application.
So my question is can this be done and if so, how do you configure spring boot to look for both the yml file and the properties and merge them into one set per environment?
As to why I want/need to use both, it is because I like the flexibility and ease of use of yml files but an internal component (for encryption) requires using the properties file set.
I did see this point being made YAML files can’t be loaded via the #PropertySource annotation
but nothing stating whether both can be used together.
Please provide detailed configuration (XML or Java config) on how to get this working.
TIA,
Scott
I can answer my own question, as it just works as you would expect. The application.yml file and the appropriate application-${profile}.properties both get loaded and merged into the environment. Spring boot just makes this work naturally.
Yes You can use both at same time in same project.
When you use both YML and properties at same time, say for example
application.yml and application.properties at same time in same
project, first application.yml will be loaded, later
application.properties will be loaded.
Important point to be noted is that if application.yml and
application.properties have same keys for example in
application.yml has spring.app.name = testYML and
application.properties has spring.app.name = testProperties at same
time in same project, then application.yml value will be overwritten
by application.properties value since it is loading at last.
And the value in spring.app.name = testProperties.
Yes, you can run both without doing any configuration.
In Spring Boot, it picks .properties or .yaml files in the following sequences :
application-{profile}.{properties|yml}
application.{properties|yml}