Multiple data sources in spring
My spring-web.xml is like this
<!-- Data source Bean -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
My DAOImpl is like this
public class BBDAOImpl extends JdbcDaoSupport implements BBDao {
#Autowired
DataSource dataSource;
#Override
public List<Map<String, Object>> getDetails(String customerId) {
String sql = "<SQL Query>";
if(BBUtil.getInstance().isNotEmpty(customerId)) {
try {
return getJdbcTemplate().queryForList(sql,customerId);
} catch (EmptyResultDataAccessException e) {
logger.error("Empty result data - getDetails");
}
} else {
// Want to configure here from second data source
}
return null;
}
Here getJdbcTemplate() method is pointing directly to the AS400 DB(But how). Now, my another requirement has come up. In the else block I want to do some data manipulation from another SQL Server.
Can any one tell me how to configure multiple DB's in this and how to use them?
This project is already developed using XML configuration and now I cannot go back to annotations config.
Thanks in advance guys.
Define two sets of jdbc templates like below:
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcTemplate2" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource2"/>
</bean>
then ahead inject them in repository like below:
#Resource("jdbcTemplate")
private JdbcTemplate jdbcTemplate;
#Resource("jdbcTemplate2")
private JdbcTemplate jdbcTemplate2;
Define two data sources in your spring data configuration file like below:
<!-- Data source Bean 1 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as4002://localhost/BB2" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
and then inject those data sources like below in your repository class and use wherever you want to :
#Autowired
DataSource dataSource;
#Autowired
DataSource dataSource1;
Now use 2nd JDBC template for your if else requirement it will use datasource 2.
You can configure an additional data source like
<bean id="dataSource1"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
<bean id="dataSource2"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.as400.access.AS400JDBCDriver" />
<property name="url" value="jdbc:as400://localhost/BB" />
<property name="username" value="ROOT" />
<property name="password" value="ROOT" />
</bean>
And then while you want to inject, you can use #Qualifier annotation.
#Autowired
#Qualifier("dataSource1")
DataSource dataSource1;
#Autowired
#Qualifier("dataSource2")
DataSource dataSource2;
Now, whenever in a class you want to use first datasource, you will use variable dataSource1, and when you want to use second datasource, you will have to use variable dataSource2.
However, now you will have to modifiy all the data access layers with #Qualifier annotation, because Spring's will have two datasource candidates to inject to in Datasource, And it is likely to ask you which one to inject.
Related
I am new to spring.
I have multiple databases but any given time one database is open depending on which database user select. So I have a bean defined as below. So my question is how do I instantiate the datasource user select and use it in my class if I use below given bean. Any ideas would be appreciated.
<bean id="dataSources" class="java.util.TreeMap">
<constructor-arg>
<map>
<entry key="dp1www" value-ref="dataSourceDp1www" />
<entry key="dp2www" value-ref="dataSourceDp2www" />
<entry key="sp0www" value-ref="dataSourceSp0www" />
<entry key="sp1www" value-ref="dataSourceSp1www" />
<entry key="sp2www" value-ref="dataSourceSp2www" />
</map>
</constructor-arg>
</bean>
<bean id="dataSourceDp1www" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:oracle:thin:#sf1-hab-ppb-ww.home.com:1525/dp1www1.home.com" />
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="username" value="select" />
<property name="password" value="######" />
</bean>
<bean id="dataSourceDp2www" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:oracle:thin:#sf1-hbl-ppb-ww.home.com:1525/dp2www1.home.com" />
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="username" value="select" />
<property name="password" value="######" />
</bean>
<bean id="dataSourceSp0www" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:oracle:thin:#sf1-hbl-ppb-ww.home.com:1525/sp0www1.home.com" />
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="username" value="select" />
<property name="password" value="######" />
</bean>
<bean id="dataSourceSp1www" class="org.apache.commons.dbcp.BasicDataSource">
<property name="url" value="jdbc:oracle:thin:#sf1-hab-ppb-ww.home.com:1525/sp1www1.home.com" />
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="username" value="select" />
<property name="password" value="######" />
</bean>
You can just use #Autowired annotation to inject TreeMap instance:
#Autowired
private TreeMap<String, DataSource> dataSources;
....
DataSource dataSource = dataSources.get("dp1www");
or you can directly inject specific data source without even creating TreeMap
#Autowired
private DataSource dataSourceDp1www;
I'm trying to learn spring framework and bean configuration and so far it seems really cool.
I'm about to create a generic class to include all my Mysql functions and it needs to contain the DataSource. My question is: Is it possible to set the datasource already in the bean configuration?
If not then I'll need to set the class as singleton, create an init function and in the init function to do the following:
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
DataSource ds = (DataSource) context.getBean("dataSource");
The question is, instead of doing that, can I 'inject' (donno if that's the right term)
it directly in the bean?
this is my bean configuration.
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/foo"/>
<property name="username" value="root"></property>
<property name="password" value="password"></property>
<property name="validationQuery" value="SELECT 1" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
<property name="initialSize" value="5" />
</bean>
<bean id="bar" class="foo.bar">
<property name="dataSource" value="<HERE_SETTING_THE_DATA_SOURCE_ABOVE>" />
</bean>
Is this possible ?
You can reference a bean like your dataSource.
Your class should have a member that can hold the dataSource:
package mypackage;
public class MyBean {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = data.Source;
}
}
Then you can inject the dataSource bean into this bean:
<beans>
<bean id="dataSource" class="org.apache.tomcat.jdbc.pool.DataSource">
<!-- set properties -->
</bean>
<bean id="myBean" class="mypackage.MyBean">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
That's it.
I have a Spring application, i want to change the data source dynamically,ie. when input a DS URL, the Spring beans and all dependency will get updated automatically.I know this is somewhat strange, but anyway i want to achieve that.
My Spring configuration as following:
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.serverName}" />
<property name="portNumber" value="${jdbc.portNumber}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="databaseName" value="${jdbc.databaseName}" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="majorDataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
The questions are:
The JDBC URL is stored in properties, which could be changed runtime.
Once the URL is changed, i need to re-create the data source and maybe the dependent objects. I could not figure out how to do it elegantly in Spring?
I have known that Spring did could dynamically route data source based on one key, but the data source URL is predefined in Spring and will not change runtime. It is not my case.
You can use spring's AbstractRoutingDataSource by extending it and overriding the method determineCurrentLookupKey() that should return the key referencing the datasource's spring bean to be used.
Take a look at this blog article on spring source's blog which will show you an example of how to use that feature.
Basically to answer your questions, what you will need to do is to define the two datasources as different spring bean in your XML config. There is no need to create one dynamically, spring will load both, and use one or the other dynamically depending on your criteria in the determineCurrentLookupKey() method.
This would lead to something like:
XML config
<!-- first data source -->
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.major.serverName}" />
<property name="portNumber" value="${jdbc.major.portNumber}" />
<property name="user" value="${jdbc.major.username}" />
<property name="password" value="${jdbc.major.password}" />
<property name="databaseName" value="${jdbc.major.databaseName}" />
</bean>
<!-- second data source -->
<bean id="minorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="${jdbc.minor.serverName}" />
<property name="portNumber" value="${jdbc.minor.portNumber}" />
<property name="user" value="${jdbc.minor.username}" />
<property name="password" value="${jdbc.minor.password}" />
<property name="databaseName" value="${jdbc.minor.databaseName}" />
</bean>
<!-- facade data source -->
<bean id="dataSource" class="blog.datasource.CustomerRoutingDataSource">
<property name="targetDataSources">
<map>
<entry key="MINOR" value-ref="minorDataSource"/>
<entry key="MAJOR" value-ref="majorDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="majorDataSource"/>
</bean>
<!-- wiring up -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:sqlmap-config.xml"/>
</bean>
Java
public class MyRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
// get the current url
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
if (request.getRequestURL().toString().endsWith("/minor"))
return "MINOR";
else
return "MAJOR";
}
}
I am not sure if this is the correct Way of doing this but what you can do is something like this.
<bean id="majorDataSource" class="org.postgresql.ds.PGSimpleDataSource">
<property name="serverName" value="dummydata" />
<property name="portNumber" value="dummydata" />
<property name="user" value="dummydata" />
<property name="password" value="dummydata" />
<property name="databaseName" value="dummydata" />
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="majorDataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
and in Java Class
public class TestTransaction {
#Autowired
private DataSourceTransactionManager manager;
private PlatformTransactionManager transactionManager;
public testExecution(DataSource ds) {
manager.setDataSource(ds);
transactionManager = manager;
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
jdbcTemplate.update();
transactionManager.commit(status);
} catch (Exception ex) {
transactionManager.rollback(status);
}
}
}
Please suggest if this Approach could Work as i am Still new to Spring
I'm having some problem with autowire and DI in general, so I hope that someone can help cause I've been stuck for days now.
This is the code:
#Service
public class TicketsController implements Controller {
private TicketManager ticketManager;
#Autowired
public void setTicketManager(TicketManager ticketManager) {
this.ticketManager = ticketManager;
}
...
}
#Service
public class SimpleTicketManager implements TicketManager {
private TicketsDao ticketsDao;
#Autowired
public void setTicketsDao(TicketsDao ticketsDao) {
this.ticketsDao = ticketsDao;
}
...
}
#Repository
public class JdbcTicketDao implements TicketsDao {
private DataSource dataSource;
#Autowired
public void setDataSource(DataSource dataSource) {
this.dataSource=dataSource;
this.jdbcTemplate = new JdbcTemplate(this.dataSource);
}
...
}
public final class AppContext {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
BeanFactory factory = context;
TicketsController ticketsController = (TicketsController) factory.getBean("ticketsController");
}
...
}
In my beans.xml I've got:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
<context:component-scan base-package="bp.dao" />
<context:component-scan base-package="bp.mvc" />
<context:component-scan base-package="bp.svc" />
<context:component-scan base-package="bp.view" />
This doesn't work and I get:
Error creating bean with name 'jdbcTicketDao': Injection of autowired dependencies failed
... nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No matching bean of type [javax.sql.DataSource] found for dependency.`
Can someone please help out with this? What am I doing wrong? It seems that autowiring is working all until the next step where it fails when injecting dataSource.
EDIT: I was playing with the code, and forgot #Autowire before setDataSource() but it is supposed to be there.
Maybe you're missing wiring configuration, try
<context:annotation-config/>
This will be due to the order of bean instance creation. Your DAO has been instantiated before the dataSource instance created.
Keep your data Source bean definition before
other way is , define your dataSource definitions in a separate xml and import that before
Try org.apache.commons.dbcp.BasicDataSource :
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://127.0.0.1:3306/mytckdb?autoReconnect=true"
p:username="user" p:password="pass" />
I use JPA so generally prefer to create EntityManagerFactory and use that
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="PU" />
<property name="jpaVendorAdapter">
<bean id="jpaAdapter"
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="${database}" />
<property name="showSql" value="true" />
<property name="generateDdl" value="false" />
</bean>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
Looks like you are using Spring 2.0 but i think context:component-scan was introduced in Spring 2.5.
Maybe update spring xml-config and spring dependencies to 2.5?
Change
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
to
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mytckdb"/>
<property name="username" value="user"/>
<property name="password" value="pass"/>
</bean>
The property is called driverClassName, not driverClass.
Also, you don't need multiple context:component-scan elements You can change
<context:component-scan base-package="bp.dao" />
<context:component-scan base-package="bp.mvc" />
<context:component-scan base-package="bp.svc" />
<context:component-scan base-package="bp.view" />
To
<context:component-scan base-package="bp.dao,bp.mvc,bp.svc,bp.view" />
At this moment I'm using DriverManagerDataSource with #Transactional annotation to manage transactions. But all transactions are very very slow, probably because data source open and close connection to db each time.
What data source should I use to speed up transaction?
I am using in my application combination of two approaches. the first one is c3p0 connection pooling, its almost the same solution as chkal sugested. The second approach is to use Spring lazyConnectionDataSourceProxy, which creates lazy loading proxy that loads connection only if you hit the database. This is very useful, when you have second level cache and you are only reading cached data and queries - database wont be hit, and you don't need to acquire connection (which is pretty expensive).
<bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driverClassName}" />
<property name="jdbcUrl" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- Pool properties -->
<property name="minPoolSize" value="5" />
<property name="initialPoolSize" value="10" />
<property name="maxPoolSize" value="50" />
<property name="maxStatements" value="50" />
<property name="idleConnectionTestPeriod" value="120" />
<property name="maxIdleTime" value="1200" />
</bean>
<bean name="lazyConnectionDataSourceProxy" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
<property name="targetDataSource" ref="dataSource" />
</bean>
DriverManagerDataSource isn't actually a connection pool and should only be used for testing. You should try BasicDataSource from Apache Commons DBCP. Something like:
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>