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
Related
I want to use LocalContainerEntityManagerFactoryBean with multiple persistence units.
applicationContext.xml:
<bean id="jpaEntityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="jpaPersistenceUnitManager"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="false"/>
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5InnoDBDialect"/>
</bean>
</property>
</bean>
<bean id="jpaPersistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="defaultPersistenceUnitName" value="pu-user”/>
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence-user.xml</value>
<value>classpath*:META-INF/persistence-customer.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key=“userDataSource" value-ref=“userDataSource"/>
<entry key="customerDataSource" value-ref=“customerDataSource"/>
</map>
</property>
<property name="defaultDataSource" ref=“userDataSource"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" primary="true">
<property name="entityManagerFactory" ref="jpaEntityManagerFactory"/>
</bean>
<tx:annotation-driven proxy-target-class="true"/>
<context:component-scan base-package="simple.user.persistence"/>
<context:component-scan base-package="simple.customer.persistence"/>
persistence xmls are defined as follows:
<persistence-unit name="pu-user" transaction-type="RESOURCE_LOCAL">
<jta-data-source>userDataSource</jta-data-source>
<non-jta-data-source>userDataSource</non-jta-data-source>
</persistence-unit>
DataSources are defined as follows:
<bean id="userDataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/user"/>
<property name="username" value="user"/>
<property name="password" value="password"/>
<property name="maxTotal" value="32"/>
<property name="maxIdle" value="32"/>
<property name="validationQuery" value="select 1"/>
</bean>
UserRepository and CustomerRepository are defined as follows:
package simple.user.persistence;
#Repository
public class UserRepository {
#PersistenceContext(unitName="pu-user”)
EntityManager em;
#Transactional
User createUser(User user) {
em.persist(user);
}
}
When I run my unit test, I am getting the following exception.
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'pu-customer' is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:698)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1175)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.orm.jpa.EntityManagerFactoryUtils.findEntityManagerFactory(EntityManagerFactoryUtils.java:139)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findNamedEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:556)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findEntityManagerFactory(PersistenceAnnotationBeanPostProcessor.java:538)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.resolveEntityManager(PersistenceAnnotationBeanPostProcessor.java:707)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor$PersistenceElement.getResourceToInject(PersistenceAnnotationBeanPostProcessor.java:680)
at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:169)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.postProcessPropertyValues(PersistenceAnnotationBeanPostProcessor.java:354)
... 60 more
Looked at the source code for EntityManagerFactoryUtils. The findEntityManagerFactory method at line 132 clearly compares only the defaultPersistentUnitName and ignores other persistent unit names defined in DeafultPersistentUnitManager. This looks to me like a bug. Please let me know what do you think.
https://github.com/spring-projects/spring-framework/blob/master/spring-orm/src/main/java/org/springframework/orm/jpa/EntityManagerFactoryUtils.java
When I've needed multiple persistence units I create multiple LocalContainerEntityManagerFactoryBean and then each LocalContainerEntityManagerFactoryBean would have a corresponding JpaTransactionManager.
The one ripple is that if you have multiple transaction managers you would need to name the transaction manager you want to use in the #Transactional annotation.
I suppose if you need to be accessing both persistence units within a single transaction you would need a JTA transaction manager to marry the 2 JPA transaction managers -- not sure what your requirements are for that though.
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.
In case there is failure in optimistic locking I expect JPA entity manager to throw javax.persistence.OptimisticLockingException
Yet when I use spring orm jpa it provides ObjectOptimisticLockingException which is not related with expected one - so the question is, WTF? Do I misunderstand JPA documentation, or did the spring guys ignored standarts?
Most funny thing is that in JUnit test case proper exception is thrown ( Wrapping StaleObjectException ),
while in web app it is FUBAR. Spring configuration is reused for unit test.
Here is some code for clarity :
<!-- JPA entity manager configuration -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${database.driver}"/>
<property name="url" value="${database.url}"/>
<property name="username" value="${database.username}"/>
<property name="password" value="${database.password}"/>
<property name="testOnBorrow" value="${database.testonborrow}"/>
<property name="validationQuery" value="${database.validationquery}"/>
</bean>
<bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="defaultDataSource" ref="dataSource"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager"/>
<property name="persistenceUnitName" value="provisioning"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="true"/>
</bean>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="${hibernate.dialect}"/>
<entry key="hibernate.hbm2ddl.auto" value="${hibernate.hbm2ddl.auto}" />
<entry key="hibernate.hbm2ddl.delimiter.type" value="InnoDB" />
<entry key="hibernate.show_sql" value="${hibernate.show_sql}" />
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<context:annotation-config/>
<tx:annotation-driven/>
It's not a bug, it's a feature. Spring's HibernateTemplate and its interceptors around repositories translate exceptions into Spring exceptions. The idea is that if you have a Hibernate-based DAO, an iBatis-based DAO or a JDBC-based DAO, they all throw the same types of exceptions, so that the client code doesn't have to care.
See http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/dao.html#dao-exceptions
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.
Using a ClassPathXmlApplicationContext object I want to get the same EntityManager that is being used by other parts of the app which get it injected via:
#PersistenceContext(unitName="accessControlDb") private EntityManager em;
Using ctx.getBean("access-emf") I can get the EntityManagerFactory which is defined in the applicationContext.xml. Using that I can create a new EntityManager, but I can't get the existing EntityManager used by the rest of the app.
I just can't figure out what code is executed to inject the value for the #PersistenceContext annotation.
<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
<bean id="innerNgsdpDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource">
<property name="driverName" value="${ngsdp.jdbc.driver}"/>
<property name="url" value="${ngsdp.jdbc.url}"/>
<property name="user" value="${ngsdp.jdbc.username}"/>
<property name="password" value="${ngsdp.jdbc.password}"/>
<property name="transactionManager" ref="jotm"/>
</bean>
<bean id="ngsdpDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource">
<property name="transactionManager" ref="jotm"/>
<property name="dataSource" ref="innerNgsdpDataSource"/>
<property name="user" value="${ngsdp.jdbc.username}"/>
<property name="password" value="${ngsdp.jdbc.password}"/>
<property name="maxSize" value="4"/>
<property name="checkLevelObject" value="2"/>
<property name="jdbcTestStmt" value="select 1 from dual"/>
</bean>
<bean id="myEmf" name="moservices" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="ngsdpDataSource"/>
<property name="persistenceXmlLocation" value="WEB-INF/moservices-persistence.xml" />
<property name="jpaVendorAdapter" ref="hibernate_jpa_vendor_adapter" />
<property name="jpaPropertyMap" ref="jpa_property_map"/>
<property name="jpaDialect" ref="hibernate_jpa_dialect"/>
</bean>
If using spring-managed transactions, you can get the current EntityManager by calling
EntityManagerFactory emFactory = ctx.getBean("access-emf");
EntityManagerHolder emHolder =
(EntityManagerHolder) TransactionSynchronizationManager.getResource(emFactory);
EntityManager em = emHolder.getEntityManager();
This is most often the current EntityManager. But this is something which should be avoided (except possibly in unit-tests), as stated in the spring docs:
To be used by resource management code but not by typical application code
Another approach might be to intercept your service calls using Spring AOP, inject the #PersistenceContext in the advice, and set in in a ThreadLocal of yours. Later, you can get it from that ThreadLocal.