Spring and Mybatis multiple data sources setup - spring

My applications uses Spring3+MyBatis3. I'm trying to setup multiple data source for it. Setup looks like:
<!-- db1 setup-->
<bean id="db1SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:configLocation="WEB-INF/mybatis/sqlMapConfig.xml"
p:dataSource-ref="db1DataSource" />
<bean id="db1SqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="db1SqlSessionFactory"/>
</bean>
<!-- db2 setup -->
<bean id="db2SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
p:configLocation="WEB-INF/mybatis/sqlMapConfig.xml"
p:dataSource-ref="db2DataSource" />
<bean id="db2SqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="db2SqlSessionFactory"/>
</bean>
In the logs, I've found this message:
No unique bean of type [org.apache.ibatis.session.SqlSessionFactory] is defined: expected single matching bean but found 2: [db1SqlSessionFactory, db2SqlSessionFactory]
I googled and looked into mybatis manuals but couldn't find way how to setup multiple data sources with mybatis.
Any ideas?

also solved ! just reference your factory bean in MapperScannerConfigurer : sqlSessionFactoryBeanName
First data source >>>>>>>
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource1"/>
</bean>
<bean id="MapperScannerConfigurer1" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.package.p1"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory1"/>
</bean>
Second data source >>>>>>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<bean id="sqlSessionFactory2" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource2"/>
</bean>
<bean id="MapperScannerConfigurer1" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.package.p2"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory2"/>
</bean>

solved, the problem was that I must specify directly reference to sqlSessionFactory
<bean id="myDao" class="org.mybatis.spring.mapper.MapperFactoryBean"
p:sqlSessionTemplate-ref="db1SqlSessionTemplate"
p:mapperInterface="my.project.domain.dao.MyDao"
p:sqlSessionFactory-ref="db1SqlSessionFactory"/>

In a DAO implementation use SqlSessionTemplate instead of SqlSessionDaoSupport. Inject bean db1SqlSessionTemplate or db2SqlSessionTemplate.
#Repository
public class TestDaoImpl implements TestDao{
#Autowired
private SqlSession db1SqlSessionTemplate;
...
db1SqlSessionTemplate.selectList("testSelect");
...
}
When extending SqlSessionDaoSupport the context Spring does not know that you use SqlSession.

Related

Reg Spring Batch Transaction

My Requirement is I need to have two datasource connected to Spring Batch Application.
1) One for Spring Batch Jobs and Executions storing
2) One for Business Data Stroing, Processing and Retreiving.
I know that there are lot of solutions for achieving this. But I have achieved by setting the second datasource as primary. The problem is the second datasource is not coming under transaction scope instead it is committing for each sql statement executing expecially through jdbctemplate.
As I can't able to edit my question. I am writing another Post in detail
My Requirement is I need to have two datasource connected to Spring Batch Application.
1) One for Spring Batch Jobs and Executions storing
2) One for Business Data Stroing, Processing and Retreiving.
In env-context.xml I have following configuration
<!-- Enable annotations-->
<context:annotation-config/>
<bean primary="true" id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/DB2XADS"/>
</bean>
<!-- Creating TransactionManager Bean, since JDBC we are creating of type
DataSourceTransactionManager -->
<bean id="transactionManager" primary="true"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- jdbcTemplate uses dataSource -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="batchTransactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager"/>
<bean id="transactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager" />
</bean>
In override-context.xml I have the following code
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- jdbcTemplate uses dataSource -->
<bean id="batchDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:/MySqlDS"/>
</bean>
<bean class="com.honda.pddabulk.utility.MyBatchConfigurer">
<property name="dataSource" ref="batchDataSource" />
</bean>
<!-- Use this to set additional properties on beans at run time -->
<bean id="placeholderProperties"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/org/springframework/batch/admin/bootstrap/batch.properties
</value>
<value>classpath:/batch/batch-mysql.properties</value>
<value>classpath:log4j.properties</value>
</list>
</property>
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
<property name="ignoreResourceNotFound" value="true"/>
<property name="ignoreUnresolvablePlaceholders" value="true"/>
<property name="order" value="1"/>
</bean>
<!-- Overrider job repository -->
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="databaseType" value="mysql"/>
<property name="dataSource" ref="batchDataSource"/>
<property name="tablePrefix" value="${batch.table.prefix}"/>
<property name="maxVarCharLength" value="2000"/>
<property name="isolationLevelForCreate" value="ISOLATION_SERIALIZABLE"/>
<property name="transactionManager" ref="batchTransactionManager"/>
</bean>
<!-- Override job service -->
<bean id="jobService" class="org.springframework.batch.admin.service.SimpleJobServiceFactoryBean">
<property name="tablePrefix" value="${batch.table.prefix}"/>
<property name="jobRepository" ref="jobRepository"/>
<property name="jobLauncher" ref="jobLauncher"/>
<property name="jobLocator" ref="jobRegistry"/>
<property name="dataSource" ref="batchDataSource"/>
</bean>
<!-- Override job launcher -->
<bean id="jobLauncher" class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
<property name="taskExecutor" ref="jobLauncherTaskExecutor" />
</bean>
<task:executor id="jobLauncherTaskExecutor" pool-size="21" rejection-policy="ABORT" />
<!-- Override job explorer -->
<bean id="jobExplorer"
class="org.springframework.batch.core.explore.support.JobExplorerFactoryBean">
<property name="tablePrefix" value="${batch.table.prefix}"/>
<property name="dataSource" ref="batchDataSource"/>
</bean>
In job-config.xml I have the following code
<context:component-scan base-package="com.honda.*">
<context:exclude-filter type="regex"
expression="com.honda.pddabulk.utility.MyBatch*" />
</context:component-scan>
I have the custom Batch configurer set. Now the problem is when I try to execute queries with jdbctemplate for update and insert it is not under transaction which means #Transactional is not working.
Rather commit is happening for each method call. The example is
#Transactional
public void checkInsertion() throws Exception{
try{
jdbcTemplate.update("INSERT INTO TABLE_NAME(COLUMN1, COLUMN2) VALUES( 'A','AF' );
throw new PddaException("custom error");
}catch(Exception ex){
int count=jdbcTemplate.update("ROLLBACK");
log.info("DATA HAS BEEN ROLLBACKED SUCCESSFULLY... "+count);
throw ex;
}
}
In the above code I am trying to insert data and immediately I am also throwing a exception which means the insert should happen but commit will not. So we will not be able to see any data but unfortunately the commit is happening. Please some one help

java spring session how to custom cookie key

I using spring session HttpSession, how can I custom cookie key, I tried this solution: Custom cookie name when using Spring Session. but it does not work, the name is SESSION still.
my config like below:
<context:annotation-config/>
<bean class="org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration"/>
<context:property-placeholder location="classpath:/env/env_test.properties"/>
<bean class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:port="${spring.redis.port}" p:hostName="${spring.redis.host}"/>
<bean id="mapSessionRepository" class="org.springframework.session.MapSessionRepository" />
<bean id="sessionRepositoryFilter"
class="org.springframework.session.web.http.SessionRepositoryFilter">
<constructor-arg ref="sessionRepository"/>
<property name="httpSessionStrategy">
<bean class="org.springframework.session.web.http.CookieHttpSessionStrategy">
<property name="cookieName" value="_session_id" />
</bean>
</property>
</bean>
You just need to add below bean to create custom cookie.
<bean class ="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookieName" value="JSESIONID"></property>
</bean>
JESSIONID - Custom Cookie Name
Please remove below configuration fr`enter code here`om xml file.
<bean id="sessionRepositoryFilter"
class="org.springframework.session.web.http.SessionRepositoryFilter">
<constructor-arg ref="sessionRepository"/>
<property name="httpSessionStrategy">
<bean class="org.springframework.session.web.http.CookieHttpSessionStrategy">
<property name="cookieName" value="_session_id" />
</bean>
</property>
</bean>
You have to create a bean with class
org.springframework.session.web.http.DefaultCookieSerializer
So inside of that bean, you define your custom properties, after you declare that bean as a property of the following:
org.springframework.session.web.http.CookieHttpSessionStrategy
By example:
<bean id="yourCookieSerializer" class="org.springframework.session.web.http.DefaultCookieSerializer">
<property name="cookieName" value="yourCustomName"/>
<property name="cookiePath" value="yourCustomPath"/>
...
</bean>
<bean id="cookieHttpSessionStrategy" class="org.springframework.session.web.http.CookieHttpSessionStrategy">
<property name="cookieSerializer" ref="yourCookieSerializer"></property>
</bean>

How to configure 2 databases in MyBatis using MyBatis-Spring

I am trying to configure 2 different datasources in my application as it is required.
My configuration in the AppContext.xml is as follows:
<bean id="dataSourceA" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver"/>
<property name="url" value="datasourceAURL"/>
<property name="username" value="aaaa"/>
<property name="password" value="pppp"/>
</bean>
<bean id="dataSourceB" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.ibm.db2.jcc.DB2Driver"/>
<property name="url" value="datasourceBURL"/>
<property name="username" value="bbbb"/>
<property name="password" value="cccc"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com/ex/myBatis/mappings" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSourceA" />
<property name="typeAliasesPackage" value="com.ex.myBatis.entities"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
</bean>
<bean id="sqlSessionFactory1" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="configDataSource" />
<property name="typeAliasesPackage" value="com.ex.myBatis.entities"/>
</bean>
<bean id="sqlSession1" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory1" />
</bean>
<bean id="callService" class="com.ex.myBatis.Service.callService">
<property name="sqlSession" ref="sqlSession1" />
</bean>
But while accessing the bean I am getting the below Exception
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'callService': Injection of autowired
dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Could not
autowire method: public final void
org.mybatis.spring.support.SqlSessionDaoSupport.setSqlSessionTemplate(org.mybatis.spring.SqlSessionTemplate);
nested exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type [org.mybatis.spring.SqlSessionTemplate] is
defined: expected single matching bean but found 2:
sqlSession,sqlSession1
Please somebody help me in figuring out the issue.
Also please suggest me if there is someother way to configure 2 Datasources.
Your configuration is mostly correct. The problem you face is that you use autowiring to inject one of callService dependencies.
Seems that you use SqlSessionDaoSupport and its sqlSessionTemplate field is autowired. There are two templates defined so spring cannot automatically wire dependencies. You need specify correct template manually.

Dynamically (Runtime) change Datasource credentials in Spring Mybatis

I want to dynamically change Datasource properties in Spring+MyBatis project.
Problem is in Spring + MyBatis integration, we cannot set the datasource properties dynamically during runtime.
Currently I'm using the following code to set the credentials:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close" p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}" p:username="${jdbc.username}"
p:password="${jdbc.password}" />
I tried options with UserCredentialsDataSourceAdapter to change the password during runtime but I cannot return back the dataSource object to use for the connection as MyBatis
ApplicationContext context = ApplicationContextUtils.getApplicationContext();
UserCredentialsDataSourceAdapter ds = (UserCredentialsDataSourceAdapter) context.getBean("dataSource");
ds.setCredentialsForCurrentThread("test", "test");
I'm stuck here, I cannot use the dataSource element ds to use for making connection for MyBatis. Please help me in resolving this issue.
I suppose that you use mybatis-spring.
Your approach with UserCredentialsDataSourceAdapter is not working because you are using connection pool, so connection is not closed after usage but are returned to the pool and reused later even that you've changed username and password.
To fix this just get rid of pool:
<bean id="targetDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
<property name="targetDataSource" ref="targetDataSource"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
and use the later bean in SqlSessionFactoryBean configuration:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
If you do not use mybatis-spring and use mybatis directly then the problem is to make mybatis use configured DataSource. This can be done by registering dataSource in JNDI and configure mybatis to get DataSource from JNDI.
<bean id="targetDataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${app.jdbc.driverClassName}" />
<property name="url" value="${app.jdbc.url}" />
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.UserCredentialsDataSourceAdapter">
<property name="targetDataSource" ref="targetDataSource" />
<property name="username" value="#{app.jdbc.username}" />
<property name="password" value="#{app.jdbc.password}" />
</bean>
<!-- Declare a transaction manager for Encounter Navigator Authenticator -->
<!-- Enable annotation style of managing transactions -->
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- define the SqlSessionFactory for Encounter Navigator Authenticator,
notice that configLocation is not needed when you use MapperFactoryBean -->
<bean id="SqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"
name="sqlSessionFactory">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="file:C:/Program Files/Apache Software Foundation/Tomcat 7.0/config/app_user/*.xml" />
<property name="configLocation" value="classpath:sqlmap-config.xml" />
</bean>
<!-- scan for MAPPERS and let them be auto-wired - For Encounter Navigator
Authenticator -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage"
value="com.upmc.health.encounternavigator.dao.authentication" />
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
</bean>
UserCredentialsDataSourceAdapter ds = (UserCredentialsDataSourceAdapter) applicationContext.getBean("dataSource");
ds.removeCredentialsFromCurrentThread();
ds.setCredentialsForCurrentThread("test", "test");
ds.setUsername("test");
ds.setPassword("test");
ds.setTargetDataSource(ds);
ds.afterPropertiesSet();
authDao.getDetails(); //This calls an interface and executes the query present in the xml file

multiple databases with Spring Data JPA

I'm trying to use Spring Data JPA with 2 databases in project. But an exception is triggered when I'm trying to run the application:
07:21:47.734 [main] ERROR o.s.web.context.ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'deviceRepository': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 2
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:342) ~[spring-orm-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294) ~[spring-beans-3.1.0.RELEASE.jar:3.1.0.RELEASE]
...
Here is my applicationContext.xml
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource1">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url1}"/>
<property name="username" value="${database.username1}"/>
<property name="password" value="${database.password1}"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="1800000"/>
<property name="numTestsPerEvictionRun" value="3"/>
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager1">
<property name="entityManagerFactory" ref="entityManagerFactory1"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager1"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory1">
<property name="persistenceUnitName" value="persistenceUnit1"/>
<property name="dataSource" ref="dataSource1"/>
</bean>
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dataSource2">
<property name="driverClassName" value="${database.driverClassName}"/>
<property name="url" value="${database.url2}"/>
<property name="username" value="${database.username2}"/>
<property name="password" value="${database.password2}"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="1800000"/>
<property name="numTestsPerEvictionRun" value="3"/>
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager2">
<property name="entityManagerFactory" ref="entityManagerFactory2"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager2"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory2">
<property name="persistenceUnitName" value="persistenceUnit2"/>
<property name="dataSource" ref="dataSource2"/>
</bean>
Here is my DAO interface:
#Repository
public interface DeviceRepository extends JpaRepository<Device, DevicePK>,
JpaSpecificationExecutor<Device> {
}
I've read a lot about #PersistenceContext but I never saw usages with JpaRepository.
Well you do have 2 entitymanagers.
I've never used JPARepository, but if it works close to its counter parts on spring-data for nosql, Spring will enhance the class, and probably inject the EM on it. Your problem is that you have 2 EM declared.
Have a look at the spring-jpa docs, they'll show you how to configure the repository for how you can add specific EMF to your repos
Long story short:
You have to create custom implementation of that interface and that implementation has to contain entity manager declared as:
#PersistenceContext(unitName = "persistenceUnit1")
private EntityManager entityManager;
if you want to use persistence unit 1.
Check this out: http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/repositories.html#repositories.custom-implementations, it also has examples and example 1.17 (little bit down the page) implements interface and has entity manager in it and that entity manager is passed on to super constructor.
You may also take a look at Spring Data - JPA, customizing Repository not working, it has implementation of interface that extends JpaRepository and it also has entity manager declared in implementation. Just add unitName to #PersistenceContext annotation and try it that way.
You may not need any custom methods (so your interface can be empty) but you do need constructor that passes on entity manager and all that tinkering with extending your own interface to bypass default automatic wiring behaviour.
One of the two datasource must be defined as primary.
<bean> has a primary attribute that can be set to true or false:
<bean primary="true|false"/>
Usually in #Configuration the #Primary annotation is placed to:
EntityManager
DataSource
TransactionManager
So you can try to add primary="true"
to the following beans:
dataSource1
transactionManager1
entityManagerFactory1

Resources