Order of Bean Creation WebMvcConfigurerAdapter Spring - java

I have convert xml to Java Class with the following code in Spring MVC. Can anyone tell me how to set order of bean creation. When i run the following code. JDBCTemplate bean create before DataSource Bean and give exception because Datasource is null.
package com.outbottle.config;
import org.apache.tomcat.dbcp.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.JstlView;
import org.springframework.web.servlet.view.UrlBasedViewResolver;
import org.apache.tomcat.jdbc.pool.DataSource;
#Configuration
#ComponentScan("com.outbottle")
#EnableWebMvc
public class Config extends WebMvcConfigurerAdapter
{
org.apache.tomcat.jdbc.pool.DataSource dataSource;
JdbcTemplate jdbcTemplate;
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Bean
public org.apache.tomcat.jdbc.pool.DataSource setDataSource()
{
dataSource = new DataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername("muhiuddin");
dataSource.setPassword("muhiuddin");
dataSource.setUrl("jdbc:oracle:thin:#172.19.0.10:1521:db10g");
dataSource.setMaxIdle(5);
dataSource.setInitialSize(5);
return dataSource;
}
#Bean
public JdbcTemplate setJdbcTemplate()
{
jdbcTemplate= new JdbcTemplate();
// setDataSource(); // if I call this function then every thing is OK.
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry)
{
registry.addResourceHandler("/resources/**").addResourceLocations("/WEB-INF/resources/*");
}
}

TL;DR: Never use instance variables
Your code should be:
#Bean
public javax.sql.DataSource dataSource()
{
final DataSource dataSource = new DataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername("muhiuddin");
dataSource.setPassword("muhiuddin");
dataSource.setUrl("jdbc:oracle:thin:#172.19.0.10:1521:db10g");
dataSource.setMaxIdle(5);
dataSource.setInitialSize(5);
return dataSource;
}
#Bean
public JdbcTemplate jdbcTemplate(final javax.sql.DataSource dataSource)
{
final JdbcTemplate jdbcTemplate= new JdbcTemplate();
// setDataSource(); // if I call this function then every thing is OK.
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
This way Spring knows that to create a JdbcTemplate bean it needs a DataSource bean; so it will decide on the order correctly.
You have bypassed Spring by using instance variables, which Spring cannot determine the creation time of.
Two other things:
An #Bean method's name sets the bean name, do you really want a DataSource bean called setDataSource?
Your beans should return the interface or abstract class rather than the specific type, this is to encourage autowiring by interface.

You can use the #DependsOn Annotation to set an order for bean creation
Something like that:
#Bean
#DependsOn("setDataSource")
public JdbcTemplate setJdbcTemplate()
{
jdbcTemplate= new JdbcTemplate();
// setDataSource(); // if I call this function then every thing is OK.
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
But you can also do call the method itself:
#Bean
public org.apache.tomcat.jdbc.pool.DataSource getDataSource()
{
DataSource dataSource = new DataSource();
dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
dataSource.setUsername("muhiuddin");
dataSource.setPassword("muhiuddin");
dataSource.setUrl("jdbc:oracle:thin:#172.19.0.10:1521:db10g");
dataSource.setMaxIdle(5);
dataSource.setInitialSize(5);
return dataSource;
}
#Bean
public JdbcTemplate setJdbcTemplate()
{
jdbcTemplate= new JdbcTemplate();
// setDataSource(); // if I call this function then every thing is OK.
jdbcTemplate.setDataSource(getDataSource());
return jdbcTemplate;
}

Related

Multiple Datasources

I have wired two datasources following examples for Oracle DB:
#Configuration
public class SpringConfigurationProperties extends DataSourceProperties {
#Bean
#Primary
#ConfigurationProperties(prefix="spring.datasource")
public DataSource primaryDatasource() {
return DataSourceBuilder.create().build();
}
#Bean
#Qualifier("primaryDatasource")
public NamedParameterJdbcTemplate primaryNpJdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
#Bean
#ConfigurationProperties(prefix="gps.bulk.load.database")
public DataSource bulkLoadDatasource() {
return DataSourceBuilder.create().build();
}
#Bean
#Qualifier("bulkLoadDatasource")
public NamedParameterJdbcTemplate bulkLoadNpJdbcTemplate(DataSource dataSource) {
return new NamedParameterJdbcTemplate(dataSource);
}
}
But I am geting the following error on startup:
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker required a single bean, but 2 were found
I think i could reproduce this error:
Parameter 1 of constructor in org.springframework.boot.autoconfigure.jdbc.DataSourceInitializerInvoker required a single bean, but 2 were found:
- myConfig: defined in file [...MyConfig.class]
- spring.datasource-org.springframework.boot.autoconfigure.jdbc.DataSourceProperties: defined in null
Seems like spring boot found 2 DataSourceProperties for the constructor DataSourceInitializerInvoker, i am not sure why, because i have only the one class that you have but when i mark my Configuration as Primary it works.
#Configuration
#Primary
public class SpringConfigurationProperties extends DataSourceProperties {
...
}
Additional for Comment .properties:
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=test
spring.datasource.password=test
spring.datasource.driver-class-name=test
gps.bulk.load.database.url=test
gps.bulk.load.database.username=test
gps.bulk.load.database.password=test
gps.bulk.load.database.driver-class-name=test

Alternative to static JdbcTemplate in Spring

I have implemented Abstract DAO Factory in Spring.
I have two autowired methods as follows:
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
At the beginning the jdbcTemplate and dataSource get right values in them. But when i call constructor of the class using the new keyword , in which the above methods are written, jdbcTemplate and dataSource get set to NULL.
But if i declare them as static then the previous right values are retained.
I wonder if there is any alternative to static in spring if i want to retain the previous values of the above two ?
You should you #Component on top of your class to get the objects values of dataSource and jdbcTemplate. you should not use new keyword of a class to get the autowired references.
Below code may help to your question.
import javax.sql.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
#Configuration
#ComponentScan("com.ledara.demo")
public class ApplicationConfig {
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dmds = new DriverManagerDataSource();
dmds.setDriverClassName("com.mysql.jdbc.Driver");
dmds.setUrl("yourdburl");
dmds.setUsername("yourdbusername");
dmds.setPassword("yourpassword");
return dmds;
}
#Bean
public JdbcTemplate getJdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSource());
return jdbcTemplate;
}
}
and below class has autowired fields of jdbcTemplate and dataSource
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
#Component
public class SampleInfo{
#Autowired(required=true)
DataSource getDataSource;
#Autowired(required=true)
JdbcTemplate getJdbcTemplate;
public void callInfo() {
System.out.println(getDataSource);
System.out.println(getJdbcTemplate);
}
}
And below is the main class
public class MainInfo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(ApplicationConfig.class);
context.refresh();
SampleInfo si=context.getBean(SampleInfo.class);
si.callInfo();
}
}

Spring boot - how to configure multiple datasources

I am trying to setup multiple data sources(MySql, Postgres & Oracle) using Spring boot. I am not using JPA. Setting up with a JdbcTemplate.
I have tried setting up something like this.
application.properties
spring.datasource.test-oracle.username=test-oracle
spring.datasource.test-oracle.password=test-password
spring.datasource.test-oracle.url=dburl/test
spring.datasource.test-oracle.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.int-oracle.username=int-oracle
spring.datasource.int-oracle.password=int-password
spring.datasource.int-oracle.url=dburl/int
spring.datasource.int-oracle.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.d.int-mysql.username=user
spring.datasource.d.int-mysql.password=password
spring.datasource.d.int-mysql.url=dburl/d
spring.datasource.d.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.int-mysql.username=user
spring.datasource.m.int-mysql.password=password
spring.datasource.m.int-mysql.url=dburl/m
spring.datasource.m.int-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.d.test-mysql.username=user
spring.datasource.d.test-mysql.password=password
spring.datasource.d.test-mysql.url=dburl/d
spring.datasource.d.test-mysql.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.m.test-mysql.username=user
spring.datasource.m.test-mysql.password=password
spring.datasource.m.test-mysql.url=dburl/m
spring.datasource.m.test-mysql.driver-class-name=com.mysql.jdbc.Driver
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration() {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
I am not sure if the above is the correct way to go about this. When I use #Primary as per the boot docs, the Bean that has #Primary is always used. Then I use the configurations in my DAO implementations like this
One of the DAO Implementation
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate jdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return jdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
How do I go about doing this.? I did see many articles which is about mutliple datasources but unfortunately the examples or solutions don't suite me.
Further to this I need to be able to query against the DB's based on some user input. So if a user provides an environment e.g., "test" or "int", how can I trigger the correct properties based on that input.
I understand that Environment is #Autowired into Spring boot and I can intercept the user input, but unsure how I should provide the plumbing between the user input and the DAO configurations.
If something is unclear or needs a bit more explanation from my side or need more code I can provide that. Any help to resolve this situation would be appreciated.Thanks
Here is complete solution to your problem ...
Your configuration classes will look like this :
MySqlConfiguration.java
#Configuration
public class MySqlConfiguration {
#Bean(name = "dMySql")
#ConfigurationProperties(prefix = "spring.datasource.d.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
#Bean(name = "mMySql")
#ConfigurationProperties(prefix = "spring.datasource.m.int-mysql")
public DataSource mysqlDrupalDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "mJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("mMySql") DataSource mMySql) {
return new JdbcTemplate(mMySql);
}
}
OracleConfiguration.java
#Configuration
public class OracleConfiguration {
#Primary
#Bean(name = "tOracle")
#ConfigurationProperties(prefix = "spring.datasource.test-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "tOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("tOracle") DataSource tOracle) {
return new JdbcTemplate(tOracle);
}
#Bean(name = "iOracle")
#ConfigurationProperties(prefix = "spring.datasource.int-oracle")
public DataSource heOracleDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "iOracleJdbc")
public JdbcTemplate jdbcTemplate(#Qualifier("iOracle") DataSource iOracle) {
return new JdbcTemplate(iOracle);
}
}
and in your DAO class , you can autowire the JdbcTemplate like this :
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("dJdbc")
private JdbcTemplate dJdbc;
#Autowired
#Qualifier("mJdbc")
private JdbcTemplate mJdbc;
#Autowired
#Qualifier("tOracleJdbc")
private JdbcTemplate tOracleJdbc;
#Autowired
#Qualifier("iOracleJdbc")
private JdbcTemplate iOracleJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL) {
return dJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
.
.
.
}
Note: Make Sure to annotate one of DataSource with #Primary annotation
My setup: spring-boot version 1.2.5.RELEASE
I succeeded in running a setup like this, with the jdbc being created with the correct DataSources by adding a #Qualifier in each JDBC method creation
So, for every JDBC method you should match the qualifying datasource like this
#Bean(name = "dJdbc")
public JdbcTemplate drupalJdbcTemplate(#Qualifier("dMySql") DataSource dMySql) {
return new JdbcTemplate(dMySql);
}
No matter you choose for #Primary, using the #Qualifier for every JDBC should work good.
Autowiring jdbcTemplates in repositories, and using #Qualifier for them is ok also.
In your DAO you could wire in additional jdbctemplates. Then at runtime you can pick which one to use.
#Repository
public class DAOImpl implements DAOInterface {
#Autowired
#Qualifier("tOracle")
private JdbcTemplate testJdbc;
#Autowired
#Qualifier("intOracle")
private JdbcTemplate intJdbc;
#Override
public Map<String, Object> getBasicStudentInfo(String MAIL, String source) {
if ("TEST".equals(source)){
return testJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}else {
return intJdbc.queryForMap(GET_BASIC_STUDENT_INFO, new Object[]{MAIL});
}
}

Two data sources in a Spring Boot application

I have a Spring Boot app for which I have configured two data sources. So far I've configured the data sources in my Application class (annotated with #EnableAutoConfiguration):
#Bean
#Primary
#ConfigurationProperties(prefix="datasource.db1")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
#ConfigurationProperties(prefix="datasource.db2")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
I also added the configuration values to application.properties:
datasource.db1.url=...
...
datasource.db2.url=...
...
Since db1 is the #Primary data source, it is chosen by default. How do I tell an interface extending JpaRepository that it should use db2 instead?
UPDATE: mentioning that my repository is an interface.
You can get the bean associated to the secondary data source from the application context.
For example in Application.java (I'm also using Spring Boot) you define:
#Bean
#ConfigurationProperties(prefix="datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
and in a service (here for calling a stored procedure) you have:
#Service
public class EngineImpl implements EngineDao {
private SetScartiProcedure setScarti;
#Autowired
public void init(ApplicationContext ctx) {
DataSource dataSource = (DataSource) ctx.getBean("secondaryDataSource");
this.setScarti = new SetScartiProcedure(dataSource);
}
public class SetScartiProcedure extends StoredProcedure {
...
}
based on this you can define several DataSourcethis way
#Bean
public LocalContainerEntityManagerFactoryBean customerEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(customerDataSource())
.packages(Customer.class)
.persistenceUnit("customers")
.build();
}
#Bean
public LocalContainerEntityManagerFactoryBean orderEntityManagerFactory(
EntityManagerFactoryBuilder builder) {
return builder
.dataSource(orderDataSource())
.packages(Order.class)
.persistenceUnit("orders")
.build();
}
and then bind each one of them to different classes that each one of them manages
#Configuration
#EnableJpaRepositories(basePackageClasses = Customer.class,
entityManagerFactoryRef = "customerEntityManagerFactory")
public class CustomerConfiguration {
...
}
#Configuration
#EnableJpaRepositories(basePackageClasses = Order.class,
entityManagerFactoryRef = "orderEntityManagerFactory")
public class OrderConfiguration {
...
}
the repositories should know which database to use by the DataSource that was bidden to the class

Multiple DataSource and JdbcTemplate in Spring Boot (> 1.1.0)

I would like to inject a specific JdbcTemplatein a Spring Boot project. I tried to follow this example for multiple DataSourceconfiguration : http://spring.io/blog/2014/05/27/spring-boot-1-1-0-m2-available-now
My code does compile and run, but only the DataSource with the #Primaryannotation is taken into account, no matter what I put as #Qualifier in the SqlServiceclass. My relevant code is the following :
DatabaseConfig.java:
#Configuration
public class DatabaseConfig {
#Bean(name = "dsSlave")
#ConfigurationProperties(prefix="spring.mysql_slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "dsMaster")
#Primary
#ConfigurationProperties(prefix="spring.mysql_master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
#Bean(name = "jdbcSlave")
#Autowired
#Qualifier("dsSlave")
public JdbcTemplate slaveJdbcTemplate(DataSource dsSlave) {
return new JdbcTemplate(dsSlave);
}
#Bean(name = "jdbcMaster")
#Autowired
#Qualifier("dsMaster")
public JdbcTemplate masterJdbcTemplate(DataSource dsMaster) {
return new JdbcTemplate(dsMaster);
}
}
And I did a quick service to try it out :
SqlService.java:
#Component
public class SqlService {
#Autowired
#Qualifier("jdbcSlave")
private JdbcTemplate jdbcTemplate;
public String getHelloMessage() {
String host = jdbcTemplate.queryForObject("select ##hostname;", String.class);
System.out.println(host);
return "Hello";
}
}
It should looks like this:
#Bean(name = "jdbcSlave")
#Autowired
public JdbcTemplate slaveJdbcTemplate(#Qualifier("dsSlave") DataSource dsSlave) {
return new JdbcTemplate(dsSlave);
}
Try to move #Qualifier annotation to the parameter on your #Bean methods for JdbcTemplate.
I guess, when you remove #Primary you end up with error, where more than one appropriate beans are presented

Categories