Annotation Value is not working in spring boot - spring-boot

I want to read few values from properties file through #Value annotation, but getting error.
Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'rcm.datasource.driverClassName' in value "${rcm.datasource.driverClassName}"
properties file
rcm.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriver
java class
#Configuration
public class RcmDBConfig {
#Value("${rcm.datasource.driverClassName}")
private String driverClassName;
#Bean(name = "rcmEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean productEntityManager() {
LocalContainerEntityManagerFactoryBean em
= new LocalContainerEntityManagerFactoryBean();
System.out.println(driverClassName);
}
}

The value can be able to inject if you have specified the component-scan package properly; otherwise spring boot can't be able to detect the packages for auto-configuration.
Ex:
#Configuration
#EnableAutoConfiguration
#ComponentScan({"com.deb.xyz.*"})
#SpringBootApplication
public class Application{
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
and your RcmDBConfig.java must be in package like com.deb.xyz.config

you can use like this
#Configuration
public class MyDatabaseConfig {
#Bean(name = "myDataSource")
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
public DataSource myDataSource() {
return DataSourceBuilder.create().type(HikariDataSource.class).build();
}
here is the yml file
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: ****
password: ***********
jdbc-url : jdbc:mysql://localhost:3306/myshema?autoReconnect=true&autoReconnectForPools=true&zeroDateTimeBehavior=convertToNull&serverTimezone=UTC
or you just want to read yml or properties files in java class
the like this
#Component
#ConfigurationProperties("spring.datasource")
#Getter
#Setter
public class SpringYMLData {
private String driver-class-name;
private String username;
private String password;
private String jdbc-url;
}

Related

Java spring boot Consider defining a bean of UserService in your configuration

I'm trying to create my first JavaSpring Security Application but getting This error:
Consider defining a bean of type 'de.JBR.mongo.models.UserService' in your configuration.
This is My Sprinboot application Class:
#ComponentScan({ "de.JBR.mongo.repositry", "de.JBR.mongo.api.models", "security.config", "security"})
#EnableMongoRepositories("de.JBR.mongo")
#SpringBootApplication
#AllArgsConstructor
public class EssensManagerApplication {
private final UserRepositry repositry;
public static void main(String[] args) {
SpringApplication.run(EssensManagerApplication.class, args);
}}
My de.JBR.mongo.models.UserService Class:
#AllArgsConstructor
#Service
public class UserService implements UserDetailsService{
private final static String USER_NOT_FOUND = "Nutzer mit Name %s nicht gefunden";
private final UserRepositry repositry;
#Override
#Bean
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return repositry.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException(String.format(USER_NOT_FOUND, username)));
}
}
Since you have added a #ComponentScan and de.JBR.mongo.models is not in the list of packages, your service is not automatically created by Spring. So, either create a #Bean in a configuration file or add the package to the #ComponentScan list.

Spring boot common application properties

Spring boot application properties needs to follow convention from https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html when we use any DB like cassandra/mongo. In case if we want to declare our own properties for DB setup instead of spring-boot convention, what are all the steps we need to do for setting up DB?
You can do this: Spring boot - custom variables in Application.properties
or you can just create your own property in your application.properties file like:
my.property.someDb.hostname=http://wherever.comand then reference to it in your code like:
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
Update 1:
If you want to create the MongoDb with your own properties you have to define the right Java Beans in an #Configuration file. For MongoDB it could look like the following:
#Configuration
public class MyMongoConfig extends AbstractMongoConfiguration{
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
#Value("${my.property.someDb.myOwnPortDefinition}")
private int myOwnPortDefinition;
#Value("${my.property.someDb.myDatabasename}")
private String myDatabasename;
#Override
protected String getDatabaseName() {
return myDatabasename;
}
#Override
#Bean
public Mongo mongo() throws Exception{
return new MongoClient(someDbHostname, myOwnPortDefinition );
}
#Bean
public MongoTemplate mongoTemplate() throws Exception{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
These are the essential steps you need in order to get a data source like Jdbc, mongodb set up in Spring Boot
Need a #Configuration class that has transaction management enabled
on it
Read the environment properties for the datasource i.e. dataSource
url, username, password etc.
Create beans for datasource, session factory, transaction manager
etc.
Once all of the above setup, use this #Configuration in your
consumer to initialize the spring application context
Here are some snippets of wiring mongodb datasource in spring boot
DataSourceConfiguration.java
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.example.xyz"})
public class DatabaseEntityConfiguration {
public static final String DATABASE_ENTITY_DATA_SOURCE = "databaseDataSource";
public static final String DATABASE_HIBERNATE_PROPERTIES = "databaseHibernateProperties";
public static final String DATABASE_ENTITY_SESSION_FACTORY = "databaseSessionFactory";
public static final String DATABASE_ENTITY_TRANSACTION_MANAGER = "databaseTransactionManager";
public static final String DATABASE_ENTITY_DB_CONFIG_DAO = "dmdatabaseDbConfigDao";
public static final String DATABASE_ENTITY_DB_CONFIG_SERVICE = "dmdatabaseDbConfigService";
private static final String ENTITY_PACKAGE = "com.example.xyz.database.entity";
#Autowired
private org.springframework.core.env.Environment environment;
#Bean(name = DATABASE_ENTITY_DATA_SOURCE)
public DataSource databaseEntitydataSource() throws PropertyVetoException {
// mongodb properties
String driverClass = environment.getProperty("databaseEntity.mongodb.driverClassName");
String mongodbUrl = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.url");
String user = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.username");
String password = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.password");
Preconditions.checkArgument(StringUtils.isNotBlank(driverClass), "The property mongodb driverClass must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(mongodbUrl), "The property mongodb mongodbUrl must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(user), "The property mongodb user must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(password), "The property mongodb password must not be null or blank");
dataSource.setDriverClass(driverClass);
dataSource.setmongodbUrl(mongodbUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = DATABASE_ENTITY_SESSION_FACTORY)
public AnnotationSessionFactoryBean databaseEntitySessionFactory() throws PropertyVetoException {
AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean();
annotationSessionFactoryBean.setDataSource(databaseEntitydataSource());
annotationSessionFactoryBean.setPackagesToScan(ENTITY_PACKAGE);
annotationSessionFactoryBean.setAnnotatedClasses(DBConfig.class);
annotationSessionFactoryBean.setHibernateProperties(databaseEntityHibernateProperties());
return annotationSessionFactoryBean;
}
#Bean(name = DATABASE_ENTITY_TRANSACTION_MANAGER)
public HibernateTransactionManager databaseEntityTransactionManager() throws PropertyVetoException {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(databaseEntitySessionFactory().getObject());
return transactionManager;
}
}

How to inject property values into Spring Boot beans

In my spring boot application, i'am trying to inject variable's value from the config file application.properties to my java class and i'm getting a null value.
here is the configuration of my application.properties file:
myapp.username=user#user.com
myapp.password=user
here is where i call the configuration entries:
#Component
public class MyClass{
#Value("${myapp.username}")
public String username;
#Value("${myapp.password}")
public String password;
public static void main(String[] args) {
System.out.println(password);
}
}
I hope there someone how did deal with the same problem, thanks.
you can use this example add bean to your config like this :
#Configuration
#ComponentScan(basePackages = "youpackagebase")
#PropertySource(value = { "classpath:application.properties" })
public class AppConfig {
/*
* PropertySourcesPlaceHolderConfigurer Bean only required for #Value("{}") annotations.
* Remove this bean if you are not using #Value annotations for injecting properties.
*/
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
and in your bean :
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
Best Regards
You are not even letting the Spring Boot container to boot (initialize) as you are are writing the code directly under main.
You should have an Application class as shown below to launch the Spring boot container properly, look here.
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
As far as my understanding you wanted to execute your code once the container is started, so follow the below steps:
Add the above Application class and then in your NetClient component class add a #Postconstruct method & this method will be called automatically once the bean is ready, refer the code below:
#Component
public class NetClient {
#Value("${bigwater.api_config.url.login}")
public String url_login;
#Value("${bigwater.api_config.url.ws}")
public static String url_ws;
#Value("${bigwater.api_config.username}")
public String username;
#Value("${bigwater.api_config.password}")
public String password;
#Postconstruct
public void init() {
//place all of your main(String[] args) method code here
}
//Add authentification() method here
}

SpringBoot and SpringJDBC multiple datasources

I am trying to use two datasources with my SpringBoot application and can't get the second datasource to autowire. I have tried many things but this is the closest I have come:
My Yaml file:
spring:
first-datasource:
url: MyURLString1
username: User
password: Password
driver-class-name: oracle.jdbc.OracleDriver
second-datasource:
url: MyURLString2
username: User
password: Password
driver-class-name: oracle.jdbc.OracleDriver
My Application Class:
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.first-datasource")
public DataSource firstDataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix = "spring.second-datasource")
public DataSource secondDataSource() {
return DataSourceBuilder.create().build();
}
}
And Finally my DAO:
#Repository
public class MyDao {
private static final String FIRST_SELECT = "select * from SomeTableInDB1";
private static final String SECOND_SELECT = "select * from AnotherTableInDB2";
#Autowired
private JdbcTemplate firstJdbcTemplate;
#Autowired
#Qualifier("secondDataSource")
private JdbcTemplate secondJdbcTemplate;
List<DB1Entity> getDB1Entity(Long id) {
return firstJdbcTemplate.query(FIRST_SELECT, new Object[] {id}, new BeanPropertyRowMapper(DB1Entity.class));
}
List<DB2Entity> getDB2Entity(Long id) {
return secondJdbcTemplate.query(SECOND_SELECT, new Object[] {id}, new BeanPropertyRowMapper(DB2Entity.class));
}
}
This is the closest I have come so far. I say it is closest because if I remove the #Qualifier then both of my dao methods actually work, assuming that the SECOND_SELECT statement is valid SQL for my DB1. Once I put in the #Qualifier for my non-primary datasouce then I get an autowire error because Spring is expecting a Datasouce object, not a JdbcTemplate object. That is weird to me as it does work with the primary datasource.
Here is my error:
Could not autowire field: private org.springframework.jdbc.core.JdbcTemplate org.my.classpath.secondJdbcTemplate; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.jdbc.core.JdbcTemplate] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=secondDataSource)}
You create the bean of type DataSource, but try to Autowire JdbcTemplate which is a mismatch. Your probably should have something like this
private JdbcTemplate jdbcTemplate1;
private JdbcTemplate jdbcTemplate2;
#Autowired
#Qualifier("firstDataSource")
public void setDataSource(DataSource dataSource){
this.jdbcTemplate1=new JdbcTemplate(dataSource);
}
#Autowired
#Qualifier("secondDataSource")
public void setDataSource(DataSource dataSource){
this.jdbcTemplate2=new JdbcTemplate(dataSource);
}
Ideally, but not a mandate, one of the datasources should be marked PRIMARY for most of the default wiring via Annotations to work. Also, we need to create TransactionManagers for each of the datasources separately otherwise Spring would not know how to enforce Transactions. Following is a complete example of how this should be done
#Primary
#Bean(name = "dataSource")
#ConfigurationProperties(prefix="datasource.mysql")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Primary
#Bean(name = "transactionManager")
public DataSourceTransactionManager transactionManager(#Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager();
}
#Bean(name = "postGresDataSource")
#ConfigurationProperties(prefix="datasource.postgres")
public DataSource postgresDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "postGresTransactionManager")
public DataSourceTransactionManager transactionManager(#Qualifier("postGresDataSource") DataSource dataSource) {
return new DataSourceTransactionManager();
}
#Transactional(transactionManager="postGresTransactionManager")
public void createCustomer(Customer cust) {
customerDAO.create(cust);
}
// specifying a transactionManager attribute is optional if we
// want to use the default transactionManager since we
// already marked one of the TM above with #Primary
#Transactional
public void createOrder(Order order) {
orderDAO.create(order);
}
Hope this helps.
Here also provide another 'not-working' situation that confused me for several days:
when configurating two data sources of same type in Springboot application, the #Qualifier doesn't work as expected to pick up right beans. It behaves like it's not recognized by Spring framework.
the reason is when using #SpringbootApplication annotation which contains #EnableAutoConfiguration annotation, which, in Springboot, will auto-configurate data sources it provides for users.
this would terribly affect #Qualifier's behavior.
In my case, following the #Aman Tuladhar answer, worked like that:
(Spring Boot 2.1.3.RELEASE)
#Configuration
public class JDBCConfig {
#Bean("first-datasource")
public JdbcTemplate paymentsJDBCTemplate(#Qualifier("first-db") DataSource paymentsDataSource){
return new JdbcTemplate(paymentsDataSource);
}
#Bean("second-datasource")
public JdbcTemplate parametersJDBCTemplate(#Qualifier("second-db") DataSource paramsDataSource){
return new JdbcTemplate(paramsDataSource);
}
#Bean("first-db")
public DataSource paymentsDataSource(Environment env) {
return buildDataSource(env, "first");
}
#Bean("second-db")
public DataSource paramsDataSource(Environment env) {
return buildDataSource(env, "second");
}
private DataSource buildDataSource(Environment env, String prop) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("spring."+prop+"-datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("spring."+prop+"-datasource.url"));
dataSource.setUsername(env.getProperty("spring."+prop+"-datasource.username"));
dataSource.setPassword(env.getProperty("spring."+prop+"-datasource.password"));
return dataSource;
}
}
... and using like that:
#Autowired #Qualifier("first-datasource")
private JdbcTemplate jdbcTemplate1;
#Autowired #Qualifier("second-datasource")
private JdbcTemplate jdbcTemplate2;
... application.yml:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${DATABASE_1_URL}
username: ${DATABASE_1_USERNAME}
password: ${DATABASE_1_PASSWORD}
second-datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: ${DATABASE_2_URL}
username: ${DATABASE_2_USERNAME}
password: ${DATABASE_2_PASSWORD}

Spring jdbc configuration

I have been trying to implement a web service using spring. This webservice will provide data access to a mySQL database using JDBC. I am trying to not use any xml configuration files, so I have come across a problem trying to connect to the database.
I am following the tutorial: http://spring.io/guides/tutorials/rest/ but I changed a few things along the way.
Now that I am trying to implement the connection with the database I get an error when trying to execute the tomcat instance, and I guess the problem is within the configurations.
Here follows some of my code:
Datasource configuration:
#Configuration
#Profile("mySQL")
#PropertySource("classpath:/services.properties")
public class MySQLDataSourceConfiguration implements DataSourceConfiguration{
#Inject
private Environment environment;
#Bean
public DataSource dataSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setPassword(environment.getProperty("dataSource.password"));
dataSource.setUrl(environment.getProperty("dataSource.url"));
dataSource.setUsername(environment.getProperty("dataSource.user"));
dataSource.setDriverClassName(environment.getPropertyAsClass("dataSource.driverClass", Driver.class).getName());
return dataSource;
}
}
the file service.properties is where I keep my configurations for the database, so when I desire to change the database I will just have to change 4 fields.
The JDBCConfiguration class for the setup of the JDBCtemplate
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSourceConfiguration.dataSource());
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
Then there is the Repository, that recieves the template.
#Transactional
#Repository
#Qualifier("jdbcRepository")
public class JdbcIndividualRepository implements IndividualsRepository{
private static final Logger LOG = LoggerFactory.getLogger(JdbcIndividualRepository.class);
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
public JdbcIndividualRepository(DataSource jdbcDataSource) {
LOG.info("JDBCRepo arg constructor");
this.jdbcTemplate = new JdbcTemplate(jdbcDataSource);
}
#Override
public Individual save(Individual save) {
String sql = "INSERT INTO Individual(idIndividual, Name) VALUES(?,?)";
this.jdbcTemplate.update(sql, save.getId(), save.getName());
return save;
}
#Override
public void delete(String key) {
String sql = "DELETE FROM Individual WHERE idIndividual=?";
jdbcTemplate.update(sql, key);
}
#Override
public Individual findById(String key) {
String sql = "SELECT i.* FROM Individual i WHERE i.idIndividual=?";
return this.jdbcTemplate.queryForObject(sql, new IndividualRowMapper(), key);
}
#Override
public List<Individual> findAll() {
String sql = "SELECT * FROM Individual";
return new LinkedList<Individual>(this.jdbcTemplate.query(sql, new IndividualRowMapper()));
}
}
Then I register the jdbc configuration in the initializer class when creating the root context of the application as follows:
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(CoreConfig.class, SecurityConfig.class, JdbcConfiguration.class);
rootContext.refresh();
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
However, the Tomcat server wont run because it can't autowire the class MySQLDataSourceConfiguration.
Anyone knows what the problem might be? I can give more details on the code, but the question is already really large.
Appreciate any kind of help!
Cheers
EDIT
Solved changing the JdbcConfiguration class to:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public IndividualsRepository createRepo(){
return new JdbcIndividualRepository(dataSource);
}
}
Remove
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
Because that's not how it's supposed to be used. Instead add to the same class the following:
#Autowired DataSource dataSource;
and use it like this: new JdbcTemplate(dataSource);
Also, try adding #ComponentScan to JdbcConfiguration class. From what I see in your code the class JdbcIndividualRepository is not picked up by anything.
In your class JdbcConfiguration, you are trying to autowire DataSourceConfiguration. I'm not really sure if that's possible - typically you should try to autwire the DataSource, not the DataSourceConfiguration.
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
Also if you have several DataSources and you're using Spring profiles to separate them, it's easier to provide all the DataSource beans in one file and annotate each bean with a different profile:
#Configuration
public class DataSourceConfig {
#Bean
#Profile("Test")
public DataSource devDataSource() {
.... configure data source
}
#Bean
#Profile("Prod")
public DataSource prodDataSource() {
... configure data source
}

Resources