How to programmatically change databases in Spring with one DataSource? - java

I'm looking to see what's the best way to use one DataSources in Spring but be able to switch the database from within the Java code? Below are my two DataSources and they go to the same database server but different databases.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDataSource" />
<property name="url"
value="jdbc:sybase:Tds:10.20.30.40:50/DATABASE_EMS" />
<property name="username" value="userid" />
<property name="password" value="derp" />
</bean>
<bean id="dataSourceMain" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDataSource" />
<property name="url"
value="jdbc:sybase:Tds:10.20.30.40:50/DATABASE" />
<property name="username" value="userid" />
<property name="password" value="derp" />
</bean>
I have them bound to their own respective bean but I'm looking at my legacy code and it's going to be VERY awkward to implement this with 2 separate beans. Is there any ideas/thoughts on how to be able to use one DataSource and switch databases when I need to?

You can do this by extending the Spring's AbstractRoutingDataSource and wrapping your existing data sources in it. Check this article for details. Quoting from the article:
The general idea is that a routing DataSource acts as an intermediary
– while the 'real' DataSource can be determined dynamically at runtime
based upon a lookup key.
Also see similar questions on SO:
Using AbstractRoutingDataSource to dynamically change the database schema/catalog
Reading from multiple Db's with same Persistence Unit?
How to create Dynamic connections (datasource) in spring using JDBC

There are many ways you can do this. For example, you can create a service class DatasourceSelectorService, and based on some input (eg: configuration file/user input) it chooses the datasource's bean accordingly.
All other classes requiring a datasource should obtain it via this DatasourceSelectorService.

Related

What is the proper way to create a datasource in a spring web application?

I have two ways to create datasource:
Inside spring context
<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/mkyongjava" />
<property name="username" value="root" />
<property name="password" value="password" />
Tomcat JNDI
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/UsersDB"/>
What are the benefits and problems of creating the datasource using spring or using tomcat jndi ?
There is nothing like good practice among this. The good one is the one which is good for use case. They both have pros and cons.
When u have use cases where u are trying to deploy the apps on a server environment where multiple apps share a common datasource or datasource components are managed by the Server administration then probably the best way to use is through a JNDI context. The server has the DataSource kept ready so that the application can directly get that object, bind to itself and use the datasource.
When u have use case where application runs inside a embedded server container or cloud containers where the server also is embedded along the build packs creating datasource with in the application and using them would be a good solution. Also in non web application environments creating datasource would be good.
To summarize, use cases where datasource is non managed by the application better to go for a JNDI context.
Try to figure out what is the deployment model of your application and figure out what is good for u. Also try to use a datasourcepool implementations to improve performance like DBCP, C3P0 or Hikarcp.
The first approach, using the class DriverManagerDataSource, is useful for test or standalone environments outside of j2ee container.
The second approach, using the jndi data soure, is recommended to be used in the j2ee container.
The below reference clearly states these approaches.
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/jdbc/datasource/DriverManagerDataSource.html
I am using it as below. Works for me since project is configured at the time of architecture design. And its live in production.
<!-- Local Apache Commons DBCP DataSource that refers to a combined database -->
<!-- (see dataAccessContext-jta.xml for an alternative) -->
<!-- The placeholders are resolved from jdbc.properties through -->
<!-- the PropertyPlaceholderConfigurer in applicationContext.xml -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="validationQuery" value="${jdbc.validationquery}"/>
<property name="testOnBorrow" value="${jdbc.testonborrow}"/>
<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>
<!-- Transaction manager for a single JDBC DataSource -->
<!-- (see dataAccessContext-jta.xml for an alternative) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
you can also refer to this. Best contribution of Genious to java developers.
https://www.mkyong.com/spring-boot/spring-boot-jdbc-oracle-database-commons-dbcp2-example/
The best practice is to not to expose the database configuration in any of the code that you have written. It is always best to configure inside the server console and expose it as a jndi. Inside spring configuration file, get the database instance as:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc"/>
and configure the database transaction manager as:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
The key note here is do not expose the database configuration.

How to use DatabaseConfiguration from apache commons with a properties file configures datasource name

I am moving our config properties from a properties file to a database using apache commons DatabaseConfiguration. Works fine. However, it obviously needs database definition, and I want to load that with a jndi datasource name that is itself a property. So I want to keep a property file with that property and one or two others, that would load first and get used by the database configuration.
I have tried about a million permutations and I can't seem to get it to work. If I hardcode the jndi name in the spring config, it works, but whether I include both in the same property configurator or separate ones, whether I use the order property or not...nothing seems to work.
Here's one of my iterations that didn't work:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/esp.properties" />
<property name="order" value="10" />
</bean>
<bean id="propertyConfigurer2" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="order" value="20" />
<property name="properties" ref="dbConfigFactory" />
</bean>
dbConfigFactory there is the factory that includes the databaseconfiguration bean. Does anyone know how to do this?

JPA with Spring Data

I'm a newbie with Java and I need to create a console application that is going to connect with 4 databases (access, vfp, mysql and sqlserver).
I started with hibernate.cfg.xml files and managed to configure them, one for each database. Then I realized that jpa was a better solution. So I changed all my hibernate files to a persistence.xml file with 4 persistence-unit.
The databases are working well, but to use them I have to create a lot of code. This is an example:
EntityManagerFactory dbPersistence =
Persistence.createEntityManagerFactory("oneOfMyDatabases");
EntityManager em = dbPersistence.createEntityManager();
Query query = em.createQuery("from ProductEntity").setMaxResults(10);
for (Object o : query.getResultList()) {
ProductEntity c = (ProductEntity) o;
System.out.println("Product " + c.getName());
}
cgPersistence.close();
I need to update one of the databases with data from the other databases.
It's a pain to create all the code like this, so I was thinking about creating repositories but I can't see how to create them with different entityManagers for each database.
I tried to inject the managers with google guice without success, but I couldn't handle how to close or where to close the persistence connection.
Finally, I've found Spring Data and it seems to be what I need, but I don't really understand how to use it.
I need some guide to get it working because I've read tons of tutorials and each of them appear to be different:
· Can I use the same persistence.xml or do I need another configuration file? I've seen that Spring Data has jpa compatibility but I'm not sure how it works.
· Is Spring Context the IOC Container of Spring Framework? Do I need it to work with Spring Data?
· What else do I need?
Thank you in advance
Each database is going to be represented by a different data source. For every data source you need a different session factory/entity manager factory.
If you want to save in more than one data source in a single transaction you then need XA transactions, therefore a Java EE or a stand-alone transaction manager, like Bitronix or Atomikos.
You have 4 different entity manager factories, you also need specific repositories for each of those:
<jpa:repositories base-package="your.company.project.repository.access" entity-manager-factory-ref="accessEntityManagerFactory"/>
<jpa:repositories base-package="your.company.project.repository.sqlserver" entity-manager-factory-ref="sqlserverEntityManagerFactory"/>
Then your application doesn't have to care which repository it uses.
The JpaTransactionManager requires one entityManagerFactory, but since you have 4 you may end up creating for of those, which I think it's undisirable.
It's better to switch to JTA instead:
<!-- 1. You define the Bitronix config ->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm"/>
<property name="warnAboutZeroResourceTransaction" value="true"/>
<property name="logPart1Filename" value="${btm.config.logpart1filename}"/>
<property name="logPart2Filename" value="${btm.config.logpart2filename}"/>
<property name="journal" value="${btm.config.journal:disk}"/>
</bean>
<!-- 2. You define all your data sources ->
<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init"
destroy-method="close">
<property name="className" value="${jdbc.driverClassName}"/>
<property name="uniqueName" value="dataSource"/>
<property name="minPoolSize" value="0"/>
<property name="maxPoolSize" value="5"/>
<property name="allowLocalTransactions" value="false"/>
<property name="driverProperties">
<props>
<prop key="user">${jdbc.username}</prop>
<prop key="password">${jdbc.password}</prop>
<prop key="url">${jdbc.url}</prop>
</props>
</property>
</bean>
<!-- 3. For each data source you create a new persistenceUnitManager and you give its own specific persistence.xml ->
<bean id="persistenceUnitManager" depends-on="transactionManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocation" value="classpath*:META-INF/persistence.xml"/>
<property name="defaultDataSource" ref="dataSource"/>
<property name="dataSourceLookup">
<bean class="org.springframework.jdbc.datasource.lookup.BeanFactoryDataSourceLookup"/>
</property>
</bean>
<!-- JpaDialect must be configured for transactionManager to make JPA and JDBC share transactions -->
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>
<!-- 4. For each data source you create a new entityManagerFactory ->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="jpaDialect" ref="jpaDialect"/>
</bean>
<!-- 5. You have only one JTA transaction manager ->
<bean id="jtaTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"
destroy-method="shutdown"/>
<!-- 6. You have only one Spring transaction manager abstraction layer ->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="jtaTransactionManager"/>
<property name="userTransaction" ref="jtaTransactionManager"/>
</bean>
If this proves too much work for your current problem, you can give a try to having 4 JPA transaction managers and see how it works.
If your schema is same across all the databases, then the best approach is to use hibernate,because hibernate provides portability between multiple databases. once you create the hbm files and pojo classes, the only thing you need to change is the dialect and your datasource.
The transactionmanager support can be provided by spring.

Having more than one data source

I haven't found a question on this topic so I'll ask. I've never actually tackled something which uses more than one data source. One example would be ETL which requires two data sources. How could such an application be designed?
Two data sources, two separate names. Inject each one by their respective bean IDs.
<bean id="fromDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${from.jdbc.driverClassName}"/>
<property name="url" value="${from.jdbc.url}"/>
<property name="username" value="${from.jdbc.username}"/>
<property name="password" value="${from.jdbc.password}"/>
</bean>
<context:property-placeholder location="from.jdbc.properties"/>
<bean id="toDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${to.jdbc.driverClassName}"/>
<property name="url" value="${to.jdbc.url}"/>
<property name="username" value="${to.jdbc.username}"/>
<property name="password" value="${to.jdbc.password}"/>
</bean>
<context:property-placeholder location="to.jdbc.properties"/>
You'd want to have a single DAO, but two instances of it - each with their own data source. One would SELECT from the source, the other would INSERT into the target.
A better way might be to forego Spring and just use bulk transfer mechanisms built into the databases.

How to define Spring datasource in controller?

Is it possible to define a datasource connector in a Spring controller ?
I'm working on a tool : synchronize a source table to a target table.
I would define source and target in my controller (to synchronize different databases - in my view I can select different source and target databases).
Actually, I define my datasource in file call : datasource.xml
My code :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
<bean id="sourceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/source"/>
<!--<property name="url" value="jdbc:mysql://linkSource"/>-->
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost/target"/>
<!--<property name="url" value="jdbc:mysql://linkTarget"/>-->
<property name="username" value="username"/>
<property name="password" value="password"/>
</bean>
</beans>
Thank you for your help !
Thank you for your help !
But I think I put my question badly.
Actually, I have in my sync-servelt.xml (just part) :
<!--sync query beans-->
<bean id="sourceDatasetQueryBean" class="ds.sync.db.SyncDatasetQuery" name="sourceDatasetsQuery">
<property name="dataSource" ref="sourceDataSource"/>
</bean>
<bean id="targetDatasetQueryBean" class="ds.sync.db.SyncDatasetQuery" name="targetDatasetsQuery">
<property name="dataSource" ref="targetDataSource"/>
</bean>
<bean id="sourceDatasetDescriptionQueryBean" class="ds.sync.db.SyncDatasetDescriptionQuery" name="sourceDatasetsDescriptionQuery">
<property name="dataSource" ref="sourceDataSource"/>
</bean>
<bean id="targetDatasetDescriptionQueryBean" class="ds.sync.db.SyncDatasetDescriptionQuery" name="targetDatasetsDescriptionQuery">
<property name="dataSource" ref="targetDataSource"/>
</bean>
...more...
And, in my controller I'm using :
#Autowired
#Qualifier("sourceDatasetQueryBean")
protected SyncDatasetQuery m_datasetQuerySource;
#Autowired
#Qualifier("targetDatasetQueryBean")
protected SyncDatasetQuery m_datasetQueryTarget;
#Autowired
#Qualifier("sourceDatasetDescriptionQueryBean")
protected SyncDatasetDescriptionQuery m_datasetDescriptionQuerySource;
#Autowired
#Qualifier("targetDatasetDescriptionQueryBean")
protected SyncDatasetDescriptionQuery m_datasetDescriptionQueryTarget;
...more...
I have 11 tables to sync between source and target...
Is there a way to group my query beans ?
My synchronizations must be performed on several databases.
For example, I have 3 sites in different places, 1 site is SOURCE (A), 2 sites are TARGET (B & C) ; with a form (made with YUI), I should be able to sync A->B and A->C.
To sum up :
1- with my form I select a SOURCE, and a TARGET (serveral databases),
2- my form send (in Ajax), the selected SOURCE and selected TARGET to my controller,
3- my controller points to the good database.
What is the best way to do this ?
Using a Factory ?
Using setDataSource ?
Thank you for help.
You should be able to use the following syntax to achieve what you want (see Spring 2.x docs):
#Autowired
#Qualifier("targetDataSource")
DataSource targetDataSource;
#Autowired
#Qualifier("sourceDataSource")
DataSource sourceDataSource;
So assuming your data sources are defined correctly it's only a matter of injecting them into your Controller:
<bean id="myController" class="...">
<property name="sourceDS" ref="sourceDataSource" />
<property name="targetDS" ref="targetDataSource" />
....
</bean>
If you don't want to be messing with spring xml files, and relay in properties or any other GUI to define those datasources at runtime, you might use:
applicationContext.getBean(bean,object[])
Be aware that is not a good practise with spring (even that it is quite handy sometimes).
This way you define your beans expecting constructor arguments and supply those arguments as part of the array. This way you create as many datasources you need at runtime getting those from wherever you want to store the information.
Finally, by using DriverManagerDataSource, and using setter, I can redefine my dataSource selected (target and source) dynamically in my controller.
I just need to use :
setDriverManagerDataSource(m_sourceDataSource);
and m_datasetQuerySource.setDataSource(dataSource); (SOURCE)
Same play with target and all tables.
I see also other way to do that :
http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
http://grails.org/Spring+Bean+Builder

Categories

Resources