I want to configure a tomcat data source in Spring Boot, The properties of the database are stored in another property file (Say dbConnection.properties) with different keys.
For example
dbConnection.properties:
DATABASE_URL=SomeURL
DATABASE_USER=SomeUser
DATABASE_PASSWORD=SomePassword
From what I understand the properties related to a data source must be specified in application.properties as:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
How do I pass the values from dbConnection.properties to application.properties?
From Spring Boot documentation
Property contributions can come from additional jar files on your classpath so you should not consider this an exhaustive list. It is also perfectly legit to define your own properties.
so you can have your own property file and it should be in your classpath,
Inject property using Value annotation
#Value("#{propFileName.propKeyName}")
All you need is override the Spring-Boot's Datasource default configuration. See the example above:
#Bean
#Primary
public DataSource dataSource() {
return DataSourceBuilder
.create()
.username("") // TODO: Get from properties
.password("") // TODO: Get from properties
.url("") // TODO: Get from properties
.driverClassName("") // TODO: Get from properties
.build();
}
In order to get from properties you can use the #Value annotation that #Saravana said you.
#Manish Kothari... try this,create a configuration class with the annotations like
#ConfigurationProperties.
#Component
#PropertySource("classpath:dbConnection.properties")
#ConfigurationProperties
public class ConfigurationClass{
//some code
}
and now it will call your DB properities... I hope this will work
There are multiple ways to do this
1.You can pass the property files from the command promopt
-Dspring.config.location=classpath:job1.properties,classpath:job2.properties
2.Way is to add #PropertySource annotation
public class AppConfig
#PropertySource("classpath:config.properties")
public class LoadDbProps{
#value("${DATABASE_USER}")
private String dbUserName;
private String dbUserName;
}
Later you can set this LoadDbProps to application.properties properties using #Bean configuration.
The below solution worked for me :
#Configuration
public class DataSourceConfig {
#Bean
public DataSource getDataSource() {
Properties properties = null;
InputStream inputStream = null;
DataSourceBuilder dataSourceBuilder =null;
try {
properties = new Properties();
inputStream = new FileInputStream("./src/main/resources/config.properties");
properties.load(inputStream);
dataSourceBuilder = DataSourceBuilder.create();
dataSourceBuilder.url(properties.getProperty("url"));
dataSourceBuilder.username(properties.getProperty("user"));
dataSourceBuilder.password(properties.getProperty("password"));
}
catch(Exception ex) {
System.out.println("CONFIG EXCEPTION :"+ex);
}
return dataSourceBuilder.build();
}
}
refer below link for more details:
https://howtodoinjava.com/spring-boot2/datasource-configuration/#:~:text=Spring%20boot%20allows%20defining%20datasource,a%20DataSource%20bean%20for%20us.
Related
I would like to achieve not-trivial bean injection implementation.
I have a custom properties file:
#Getter
#Setter
#ConfigurationProperties
public class DatabaseProperties {
private String url;
private String username;
private String password;
}
I Here is the configuration file:
#Configuration
#EnableConfigurationProperties(DatabaseProperties.class)
public class DBConfig {
#Bean
#ConfigurationProperties(prefix = "datasource.database1")
public JdbcTemplate jdbcTemplateDatabase1(DatabaseProperties databaseProperties) {
DataSource dataSource = new DriverManagerDataSource(
databaseProperties.getUrl(),
databaseProperties.getUsername(),
databaseProperties.getPassword());
return new JdbcTemplate(dataSource);
}
#Bean
#ConfigurationProperties(prefix = "datasource.database2")
public JdbcTemplate jdbcTemplateDatabase2(DatabaseProperties databaseProperties) {
DataSource dataSource = new DriverManagerDataSource(
databaseProperties.getUrl(),
databaseProperties.getUsername(),
databaseProperties.getPassword());
return new JdbcTemplate(dataSource);
}
}
The goal I want to achieve is to instantiate a new DatabaseProperties instance based on prefix.
There are two possible solutions:
create two beans of type DatabaseProperties using corresponding prefixes and two JdbcTemplate beans where parameter is qualified DatabaseProperties bean accordingly.
in each JdbcTemplate bean provide 3 parameters (String url, String username, String password) and inject them through #Value
BUT Is it possible to get rid of creating DatabaseProperties beans for each JdbcTemplate or using #Value ?
You don't need create a DatabaseProperties. Spring already does this for us on datasources and proprierties variable
#Configuration
public class ConfigDataSource {
#Bean("datasource-1") // this name will qualify on #autowired
#ConfigurationProperties(prefix="spring.datasource.yourname-datasource-1") // this is the name for the prefix for datasource on .properties settings
public DataSource dataSourcePostgres() {
return DataSourceBuilder.create().build();
}
#Bean("datasource-2") // this name will qualify on #autowired
#ConfigurationProperties(prefix="spring.datasource.yourname-datasource-2") // this is the name for the prefix for datasource on .properties settings
public DataSource dataSourceMySql() {
return DataSourceBuilder.create().build();
}
}
.properties
# Its required use the same name declared in bean
spring.datasource.yourname-datasource-1.url=...
spring.datasource.yourname-datasource-1.jdbcUrl=${spring.datasource.yourname-datasource-1}
spring.datasource.yourname-datasource-1.username=user
spring.datasource.yourname-datasource-1.password=pass
spring.datasource.yourname-datasource-1.driver-class-name=your.driver
spring.datasource.yourname-datasource-2.url=...
spring.datasource.yourname-datasource-2.jdbcUrl=${spring.datasource.yourname-datasource-2}
spring.datasource.yourname-datasource-2.username=user
spring.datasource.yourname-datasource-2.password=pass
spring.datasource.yourname-datasource-2.driver-class-name=your.driver
using on services
#Awtowired
#Qualifier("datasource-1")
private DataSource dataSource1;
#Awtowired
#Qualifier("datasource-2")
private DataSource dataSource2;
public testJdbcTemplate(){
// You can qualifier JdbcTemplate below on bean and not necessary need instance on service
JdbcTemplate jdbcTemplateDatasource1 = new JdbcTemplate(dataSource1);
JdbcTemplate jdbcTemplateDatasource2 = new JdbcTemplate(dataSource2);
}
To my knowledge, there is no way around the fact that if you want to have access to multiple databases Spring will not be able to do the magic for you. You will need to create the two JdbcTemplate Spring-managed beans and then inject them where needed using the #Qualifier annotation.
This approach has two benefits:
It actually work;
You are explicit about what you are doing. A Spring application has already a good load of magic happening in there, so we might want to avoid some additional one when we need something a little bit more custom and that is not that complex to achieve.
i'm developing an spring 4.0 REST application and it works fine, but when i try to make some test it fails at reading propety values from placeholders (they work fine if i run the app normally)
Some of the properties of the app come from files and other from database so i have configured an PropertiesPropertySource to read them from db. I was unable to config it by xml, so i did it in a #Configuration class:
public class AppConfig {
private static final Logger logger = LoggerFactory.getLogger(AppConfig.class);
#Inject
private org.springframework.core.env.Environment env;
#Autowired
private DataSource dataSource;
#PostConstruct
public void initializeDatabasePropertySourceUsage() {
MutablePropertySources propertySources = ((ConfigurableEnvironment) env).getPropertySources();
try {
DatabaseConfiguration databaseConfiguration = new DatabaseConfiguration(dataSource, "[TABLE]", "property", "value");
CommonsConfigurationFactoryBean commonsConfigurationFactoryBean = new CommonsConfigurationFactoryBean(databaseConfiguration);
Properties dbProps = (Properties) commonsConfigurationFactoryBean.getObject();
PropertiesPropertySource dbPropertySource = new PropertiesPropertySource("dbPropertySource", dbProps);
propertySources.addFirst(dbPropertySource);
} catch (Exception e) {
logger.error("Error during database properties setup:"+e.getMessage(), e);
throw new RuntimeException(e);
}
}
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
In the test class i load the context config with this annotation:
#ContextConfiguration(classes = {TestConfig1.class,AppConfig.class,TestConfig2.class})
As i'm mixing XML and java configurations i had to create the TestConfigX classes which load the XMLs with this annotation:
#ImportResource([PATH TO config xml])
TestConfig1 has the DatasourceBean definition and it works fine
AppConfig config the PropertiesPropertySource to read the placeholders from DB
TestConfig2 config the rest of the app, which uses the placeholders ( #Value=${XXXX}), but is unable to read the values: Could not resolve placeholder XXXX
If i omit TestConfig2 i can use the placeholders from DB in the test without problem. But obviously i´m not testing anything.
What i'm doing wrong?
I've the following configuration file
#Configuration
#ComponentScan(basePackages = "com.foo")
#EnableTransactionManagement
public class AppSpringConfiguration {
#Autowired
private Environment env;
#Autowired
private ApplicationContext appContext;
#Value("#{cvlExternalProperties['dbDriverClassName']}")
private String dbDriverName;
#Bean
public PropertiesFactoryBean cvlExternalProperties() {
PropertiesFactoryBean res = new PropertiesFactoryBean();
res.setFileEncoding("UTF-8");
res.setLocation(new FileSystemResource(env.resolvePlaceholders("${MY_ENV_VAR}") + "external.properties"));
return res;
}
#Bean
public BasicDataSource datasource() {
BasicDataSource basicDataSource = new BasicDataSource();
basicDataSource.setDriverClassName("myDriverClassName");
basicDataSource.setUrl("MyDbUrl");
basicDataSource.setUsername("myUser");
basicDataSource.setPassword("myPass");
return basicDataSource;
}
}
And in the external properties file I've
dbUrl=jdbc:mysql://localhost:3306/someDb
dbUser=someUser
dbPassword=somePass
dbDriverClassName=com.mysql.jdbc.Driver
In which way I can use the cvlProperties inside the datasource() method?
I've tried
env.getProperty("dbDriverClassName")
env.getProperty("#cvlProperties['dbDriverClassName']")
But I'm not able to retrieve the properties.
The field dbDriverName is correctly filled, so that means the bean declaration is ok.
I want to use the PropertyFactoryBean class because in this way I can specify the encoding to use.
If I use the the following annotation on the top of the configuration class
#PropertySource("file:${MY_ENV_VAR}/external.properties")
I'm able to retrieve the properties with this piece of code
env.getProperty("dbDriverClassName")
But the encoding used by the PropertySource annotation is the windows default, and for me is not correct.
Can you help me?
At the moment the solution(that I don't love so much) is to declare the properties by using the annotation #Value
#Value("#{cvlExternalProperties['dbDriverClassName']}")
private String dbDriverClassName;
and then using it inside the java class
I need to get the following system property within one of my configuration files: -Dspring.profiles.active="development". Now I have seen countless of people suggest that this can be done with Spring Expression Language, but I cannot get it to work. Here is what I have tried (plus many variations hereof).
#Configuration
#ComponentScan(basePackages = { ... })
public class AppConfig {
#Autowired
private Environment environment;
#Value("${spring.profiles.active}")
private String activeProfileOne;
#Value("#{systemProperties['spring.profiles.active']}")
private String activeProfileTwo;
#Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
Resource[] resources = {
new ClassPathResource("application.properties"),
new ClassPathResource("database.properties")
// I want to use the active profile in the above file names
};
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
propertySourcesPlaceholderConfigurer.setLocations(resources);
propertySourcesPlaceholderConfigurer.setIgnoreUnresolvablePlaceholders(true);
return propertySourcesPlaceholderConfigurer;
}
}
All of the properties are NULL. The same happens if I try to access any other system property. I can access them without problems by invoking System.getProperty("spring.profiles.active"), but this is not as nice.
Many examples that I found configure the PropertySourcesPlaceholderConfigurer to also search system properties, so maybe this is why it does not work. However, those examples are in Spring 3 and XML configuration, setting a property that no longer exists on the class. Perhaps I have to call the setPropertySources method, but I am not entirely sure how to configure this.
Either way, I found multiple examples that suggest that my approach should work. Believe me, I searched around a lot. What's wrong?
Just autowire an instance of Spring's Environment interface, as you're actually doing and then ask what the active environment is:
#Configuration
public class AppConfig {
#Autowired
private Environment environment;
#Bean
public PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
List<String> envs = Arrays.asList(this.environment.getActiveProfiles());
if (envs.contains("DEV")) {
// return dev properties
} else if (envs.contains("PROD")) {
// return prod properties
}
// return default properties
}
}
Can you please tell me how to use Spring Javaconfig to directly load/autowire a properties file to a java.util.Properties field?
Thanks!
Later edit - still searching for the answer:
Is it possible to load with Spring JavaConfig a properties file directly into a java.util.Properties field?
The XML base Way:
in spring config:
<util:properties id="myProperties" location="classpath:com/foo/my-production.properties"/>
in your class:
#Autowired
#Qualifier("myProperties")
private Properties myProperties;
JavaConfig Only
It looks like there is an annotation:
#PropertySource("classpath:com/foo/my-production.properties")
Annotating a class with this will load the properties from the file in to the Environment. You then have to autowire the Environment into the class to get the properties.
#Configuration
#PropertySource("classpath:com/foo/my-production.properties")
public class AppConfig {
#Autowired
private Environment env;
public void someMethod() {
String prop = env.getProperty("my.prop.name");
...
}
I do not see a way to directly inject them into the Java.util.properties. But you could create a class that uses this annotation that acts as a wrapper, and builds the properties that way.
declare a PropertiesFactoryBean.
#Bean
public PropertiesFactoryBean mailProperties() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("mail.properties"));
return bean;
}
Legacy code had following config
<bean id="mailConfiguration" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:mail.properties"/>
</bean>
Converting that to Java config is super easy as shown above.
It is an old subject but there are also a more basic solution.
#Configuration
public class MyConfig {
#Bean
public Properties myPropertyBean() {
Properties properties = new Properties();
properties.load(...);
return properties;
}
}
There is also this approach for injecting properties directly using xml configurations. The context xml has this
<util:properties id="myProps" location="classpath:META-INF/spring/conf/myProps.properties"/>
and the java class just uses
#javax.annotation.Resource
private Properties myProps;
Voila!! it loads.
Spring uses the 'id' attribute in xml to bind to the name of variable in your code.
You can try this
#Configuration
public class PropertyConfig {
#Bean("mailProperties")
#ConfigurationProperties(prefix = "mail")
public Properties getProperties() {
return new Properties();
}
}
Make sure to define properties in application.properties
application.yml:
root-something:
my-properties:
key1: val1
key2: val2
Your type-safe pojo:
import java.util.Properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "root-something")
public class RootSomethingPojo {
private Properties myProperties;
Your container configuration:
#Configuration
#EnableConfigurationProperties({ RootSomethingPojo .class })
public class MySpringConfiguration {
This will inject the key-value pairs directly into the myProperties field.