I've started development of a new Spring 3.2.4 application and am trying to use Java based configuration instead of XML files as I have used in the past. However, I am having trouble making the transition.
Using XML, I would code it as follows:
<!-- application datasource -->
<bean id="dataSource.jndi" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton" lazy-init="true">
<property name="jndiName" value="java:comp/env/jdbc/liment" />
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="transactionManager"/>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="entityManagerFactory">
<property name="persistenceUnitName" value="persistenceUnit"/>
<property name="dataSource" ref="dataSource.jndi"/>
</bean>
However, I am very much stuck trying to figure out how to do this in Java. I'm trying to replicate the configuration, but running into trouble:
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(basePackages={"com.ia"})
public class AppConfigJPA {
#Bean
public DataSource dataSource() {
// configure and return the necessary JDBC DataSource
JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
dataSource.setJndiName("java:comp/env/jdbc/liment");
try {
dataSource.afterPropertiesSet();
} catch (IllegalArgumentException | NamingException e) {
// rethrow
throw new RuntimeException(e);
}
return (DataSource)dataSource.getObject();
}
#Bean
public EntityManagerFactory entityManagerFactory(){
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceUnitName("persistenceUnit");
emf.setDataSource(dataSource());
emf.afterPropertiesSet
return emf.getObject();
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory());
}
}
However, I get the following error message:
Caused by: java.lang.IllegalStateException: No persistence exception translators found in bean factory. Cannot perform exception translation.
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.detectPersistenceExceptionTranslators(PersistenceExceptionTranslationInterceptor.java:142)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.setBeanFactory(PersistenceExceptionTranslationInterceptor.java:117)
at org.springframework.data.repository.core.support.PersistenceExceptionTranslationRepositoryProxyPostProcessor.<init>(PersistenceExceptionTranslationRepositoryProxyPostProcessor.java:44)
at org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport.setBeanFactory(TransactionalRepositoryFactoryBeanSupport.java:85)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeAwareMethods(AbstractAutowireCapableBeanFactory.java:1502)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1470)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
... 33 more
What am I missing or doing wrong?
EDIT
Following #SotiriosDelimanolis response, I have modified my code to read the following:
#Autowired DataSource dataSource;
#Autowired EntityManagerFactory entityManagerFactory;
#Bean
public JndiObjectFactoryBean dataSource() {
// configure and return the necessary JDBC DataSource
JndiObjectFactoryBean dataSource = new JndiObjectFactoryBean();
dataSource.setJndiName("java:comp/env/jdbc/josak");
return dataSource;
}
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceUnitName("persistenceUnit");
emf.setDataSource(dataSource);
return emf;
}
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory);
}
But am getting Autowired exceptions instead now:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: javax.sql.DataSource com.ia.system.configuration.AppConfigJPA.dataSource; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:514)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
... 31 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:988)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:858)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:770)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:486)
... 33 more
please refer below link -
http://forum.spring.io/forum/spring-projects/container/724356-how-to-use-javaconfig-to-declare-a-jndi-datasource
the datasource need to be created like this -
#Bean
public DataSource dataSource() {
final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
dsLookup.setResourceRef(true);
DataSource dataSource = dsLookup.getDataSource("jdbc/yourJdbcGoesHere");
return dataSource;
}
This is a weird design (for PersistenceExceptionTranslator) that I don't immediately understand, but here is the solution.
Your LocalContainerEntityManagerFactoryBean is a FactoryBean but also a PersistenceExceptionTranslator (implements both). But you aren't putting the LocalContainerEntityManagerFactoryBean into your context, you are only getting its created object.
Instead of
#Bean
public EntityManagerFactory entityManagerFactory(){
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceUnitName("persistenceUnit");
emf.setDataSource(dataSource());
emf.afterPropertiesSet
return emf.getObject();
}
do
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
emf.setPersistenceUnitName("persistenceUnit");
emf.setDataSource(dataSource());
return emf;
}
#Autowired
private EntityManagerFactory entityManagerFactory;
#Bean
public PlatformTransactionManager transactionManager() {
return new JpaTransactionManager(entityManagerFactory);
}
Spring will take care of calling afterPropertiesSet() and getObject() to put a EntityManagerFactory bean into the context.
Basically you end up with two beans, a EntityManagerFactory and a LocalContainerEntityManagerFactoryBean. Your JPA configuration requires a PersistenceExceptionTranslator bean in the context. That will be satisfied by LocalContainerEntityManagerFactoryBean.
FYI, you can do the same thing for your JndiObjectFactoryBean or any other FactoryBean.
Related
I am upgrading my JBoss server from 5 to 7 and am now incorporating Spring 4. I am having some trouble using Spring's #Transactional annotation. It does not appear to be working. I am also trying to use a java based configuration file instead of an xml file (I believe I can get away without using any xml, but correct me if I am wrong). The problem is that nothing is being saved in my db, leading me to believe that the #Transactional isn't working. Here is my config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public FirstTestBean firstTestBean() {
return new FirstTestBean();
}
#Bean
public TestService testService() {
return new TestServiceImpl();
}
#Bean
public SomethingDAO somethingDAO(){
return new SomethingDAOImpl();
}
#Bean
public GeneralDAO generalDAO(){
return new GeneralDAOImpl();
}
Here is a test class with the #Transactional method:
//#RequestScoped
#ManagedBean(name="firstTestBean")
#Component
public class FirstTestBean {
private EntityManager em;
private EntityManagerFactory emf;
#Transactional
public String transactionalTest() {
//ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
public EntityManager getEntityManager() {
if (em == null) {
emf = Persistence.createEntityManagerFactory("xxx");
em = emf.createEntityManager();
}
return em;
}
I am also using Hibernate 4, which is compatible with Spring. I commented out the ApplicationContext because I have that running separately on the start up of JBoss. I was using that earlier to access a bean, but I have since simplified things in order to get the #Transactional working and thus do not need it here. The #ComponentScan does not need parameters because these classes are in the same package.
Any help would be greatly appreciated. Thanks!
Updates
I've made some of the changes suggested. Things appear to be moving in the right direction.
Here are my updated files:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
public String getTestString() {
ApplicationContext context = new AnnotationConfigApplicationContext(SpringBeanConfiguration.class);
TestService testService = context.getBean(TestService.class);
testService.transactionalTest();
return "adfs";
}
public String test() {
return "firstTestBean works";
}
}
Note - some of these classes will be run outside of the Jboss application server as standalone applications, so for that reason, I am staying away from FacesContext when instantiating the TestService in FirstTestBean, as Spring beans work in standalone, but FacesContext beans do not.
#Component
#Transactional
public class TestServiceImpl implements TestService {
public GeneralDAO generalDAO;
//#Autowired
private EntityManager em;
//#Autowired
private EntityManagerFactory emf;
public TestServiceImpl(){}
public String transactionTest() {
Something something = new Something();
getEntityManager().persist(something);
return "dkljs";
}
#autowired on the EntityManager and EntityManagerFactory did not work - I received an error saying No Qualified Bean of type EntityManager when it was annotated with the #autowired like suggested.
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration implements TransactionManagementConfigurer {
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
String hibernatePropsFilePath = "/[path]/hibernate.cfg.xml";
File hibernatePropsFile = new File(hibernatePropsFilePath);
org.hibernate.cfg.Configuration cfg = new org.hibernate.cfg.Configuration().configure(hibernatePropsFile);
SessionFactory sessionFactory = cfg.buildSessionFactory();
HibernateTransactionManager txManager = new HibernateTransactionManager(sessionFactory);
txManager.setNestedTransactionAllowed(true);
return txManager;
}
}
The error I am getting now is:Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.context.event.internalEventListenerProcessor': Initialization of bean failed
; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration':
Injection of autowired dependencies failed; nested exception is org.hibernate.service.UnknownUnwrapTypeException: Cannot unwrap to requested type [javax.sql.DataSource]
I take it this means that the #Transactional is at least being recognized, but I'm having some issues getting this to work. Any further advice would be greatly appreciated.
More Updates
Found this article: http://www.baeldung.com/the-persistence-layer-with-spring-and-jpa#javaconfig
and followed it. My new config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration { //implements TransactionManagementConfigurer {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
Good news is that I am no longer having deployment issues. The logging also suggests that my changes are having some effect on the server. Unfortunately, nothing is being saved, so it is still not quite working.
The first thing that draws my attention is the use of #Transactional and #Component on a ManagedBean.
JSF and Spring are definately made to work together but i never saw them used this way on many projects i was working on.
Im not sure if that is the cause of your problems but please consider changing that.
I would do like this:
a) Define some service layer where you would wrap your calls with transactions and inject the JPA classes:
#Component
#Transactional
class Service{
#Autowired
private EntityManager em;
#Autowired
private EntityManagerFactory emf;
public String serviceMethod(..){ .. }
}
b) Inject that to Jsf's ManagedBean while removing the unnecessary annotations:
#ManagedBean(name="firstTestBean")
public class FirstTestBean {
#ManagedProperty("#{service}")
private Service service;
public String transactionalTest() {
return service.serviceMethod();
}
}
Ok, so I finally got it (Check out my OP for all updates that led me to this point).
Here's my final config file:
#Configuration
#ComponentScan
#EnableTransactionManagement
public class SpringBeanConfiguration {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("myPersistenceContext");
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "xxx.xxx.xxx" });
JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
// em.setJpaProperties(additionalProperties());
return em;
}
#Bean
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/jboss_test");
dataSource.setUsername( "root" );
dataSource.setPassword( "root" );
return dataSource;
}
#Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf){
JtaTransactionManager transactionManager = new JtaTransactionManager();
// transactionManager.setEntityManagerFactory(emf);
return transactionManager;
}
#Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}
So first, I added in
em.setPersistenceUnitName("myPersistenceContext");
This gave me the error:
Spring IllegalStateException: A JTA EntityManager cannot use getTransaction()
From here, I did some research (Spring IllegalStateException: A JTA EntityManager cannot use getTransaction())
and changed
JpaTransactionManager transactionManager = new JpaTransactionManager();
to
JtaTransactionManager transactionManager = new JtaTransactionManager();
Also, my persistence.xml file is:
<persistence-unit name="myPersistenceContext">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:jboss/datasources/myDS</jta-data-source>
<class>xxx.xxx.xxx.Something</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.id.new_generator_mappings" value="false"/>
<property name="hibernate.classloading.use_current_tccl_as_parent" value="false"/>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossAppServerJtaPlatform" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.archive.autodetection" value="class, hbm" />
</properties>
</persistence-unit>
Thanks to Maciej Kowalski for your help - you pushed me in the right direction.
I'm creating a standalone Java application that uses Spring Data with JPA.
Part of the class that creates the factory for the EntityManagerFactory is below:
#Configuration
#Lazy
public class JpaConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(MultiTenantConnectionProvider connProvider, CurrentTenantIdentifierResolver tenantResolver) {
...
}
The problem is: I can only detect the Hibernate Dialect after the ApplicationContext is initialized, because this information is read from an external configuration service.
Since #Lazy did not work, is there any strategy to avoid creating this bean before it is used, i.e, only create it when another bean injects an instance of EntityManager?
I stumbled upon this issue recently and found a solution that worked. Unfortunately "container" managed beans will be initialized during startup and #Lazy is ignored even if the EntityManager is not injected anywhere.
I fixed it by using an in-memory H2 DB to construct the factory bean during startup and changed it later. I think here's what you can do for your issue.
pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.199</version>
</dependency>
Source code:
#Configuration
public class DataSourceConfig {
#Bean
public HikariDataSource realDataSource() {
...
}
#Bean
public DataSource localH2DataSource() {
return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
}
#Bean
public LocalContainerEntityManagerFactoryBean myEntityManagerFactory() throws PropertyVetoException {
LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
factoryBean.setDataSource(localH2DataSource());
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setShowSql(true);
factoryBean.setJpaVendorAdapter(jpaVendorAdapter);
return factoryBean;
}
}
#Component
#Lazy
public class Main {
#Autowired
private LocalContainerEntityManagerFactoryBean emf;
#Autowired
private HikariDataSource realDataSource;
#PostConstruct
private void updateHibernateDialect() {
// read the external config here
emf.setDataSource(realDataSource);
Properties jpaProperties = new Properties();
jpaProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.DB2Dialect");
factoryBean.setJpaProperties(jpaProperties);
}
}
I have a scenario where datasource are dynamically created using Datasource Factory. So depending upon who accesses the system, DatasourceFacory(custom datasource factory) returns a corresponding datasource.
Now, with this strategy, how do I maintain Spring Transaction? Using a #Transactional annotation needs a fixed datasource with transaction manager configured.
I would like to continue to use #Transactional in a service method and not worry about having to maintain transaction myself.
I would think that I would have to extend some spring class and inject the datasource when the system starts.
I am using Spring and JdbcTemplate in my project. No Hibernate. Any help would be appreciated.
You have to use AbstractRoutingDataSource to link it with your transaction manager, this kind of datasource allows you use different datasources choosing them in runtime.
After that spring handles the transaction properly. You can check this kind of DataSource in the Spring Documentation.
The other option is has different transactionManagers.
For no-xml lovers
#Bean(name = "mysqldatasource1")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(classname);
dataSource.setUrl(url);
dataSource.setUsername(usernmae);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = "mySqlJdbcTemplate1")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
#Bean(name="mysqlTransaction1")
#Autowired
DataSourceTransactionManager tm1(#Qualifier ("mysqldatasource") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
#Bean(name = "mysqldatasource2")
public DataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(classname);
dataSource.setUrl(url);
dataSource.setUsername(usernmae);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = "mySqlJdbcTemplate2")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
#Bean(name="mysqlTransaction2")
#Autowired
DataSourceTransactionManager tm1(#Qualifier ("mysqldatasource2") DataSource datasource) {
DataSourceTransactionManager txm = new DataSourceTransactionManager(datasource);
return txm;
}
You can create multiple Transaction Managers with different datasources and then access them in #Transactional annotation as follows:
<bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
<qualifier value="txManager1"/>
</bean>
Access it as:
#Transactional("txManager1")
Similarly,
<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2"/>
<qualifier value="txManager2"/>
</bean>
Access it as:
#Transactional("txManager2")
As there can be only one annotation-driven TxManager, do not forget to remove this:
<tx:annotation-driven transaction-manager="transactionManager" />
I need to use one database for queries (non-modifying) and one for commands (modifying). I am using Spring Data JPA, so I have two configuration classes:
#Configuration
#EnableJpaRepositories(value = "com.company.read",
entityManagerFactoryRef = "readingEntityManagerFactory",
transactionManagerRef = "readingTransactionManager")
#EnableTransactionManagement
public class SpringDataJpaReadingConfiguration {
#Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
return Persistence.createEntityManagerFactory("persistence.reading");
}
#Bean(name = "readingExceptionTranslator")
public HibernateExceptionTranslator readingHibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean(name = "readingTransactionManager")
public JpaTransactionManager readingTransactionManager() {
return new JpaTransactionManager();
}
}
#Configuration
#EnableJpaRepositories(value = "com.company.write",
entityManagerFactoryRef = "writingEntityManagerFactory",
transactionManagerRef = "writingTransactionManager")
#EnableTransactionManagement
public class SpringDataJpaWritingConfiguration {
#Bean(name = "writingEntityManagerFactory")
public EntityManagerFactory writingEntityManagerFactory() {
return Persistence.createEntityManagerFactory("persistence.writing");
}
#Bean(name = "writingExceptionTranslator")
public HibernateExceptionTranslator writingHibernateExceptionTranslator() {
return new HibernateExceptionTranslator();
}
#Bean(name = "writingTransactionManager")
public JpaTransactionManager writingTransactionManager() {
return new JpaTransactionManager();
}
}
In my repository I sometimes need to decide with EntityManager to use like so:
#Repository
public class UserReadingRepository {
#PersistenceContext(unitName = "persistence.reading")
private EntityManager em;
// some useful queries here
}
I am using persistence unit's name as defined in my persistence.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
version="2.0">
<persistence-unit name="persistence.reading" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>ReadingDS</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
<persistence-unit name="persistence.writing" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<non-jta-data-source>WritingDS</non-jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
Spring throws org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading' is defined. Oddly, it looks like Spring tries to instantiate a bean with persistence unit name? Did I misconfigure something?
UPDATE: When I remove unitName = "persistence.reading" from #PersistenceContext annotation, I will get following error instead:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [javax.persistence.EntityManagerFactory] is defined: expected single matching bean but found 2: readingEntityManagerFactory,writingEntityManagerFactory
UPDATE 2: Rohit suggested (in the comment) to wire EntityManagerFactory instead. So I tried to do the following:
#PersistenceUnit(unitName = "persistence.reading")
private EntityManagerFactory emf;
but Spring only reports: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'persistence.reading' is defined
FINAL FIX:
Thanks to Vlad's answer, I was able to update the code to use the following (just make sure you define your dataSource bean as well):
#Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("persistence.reading");
em.setDataSource(dataSource());
em.setPackagesToScan("com.company");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
The EntityManageFactory is not properly configured. You should use a LocalContainerEntityManagerFactoryBean instead:
#Bean(name = "readingEntityManagerFactory")
public EntityManagerFactory readingEntityManagerFactory() {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setPersistenceUnitName("persistence.reading");
em.setDataSource(dataSource());
em.setPackagesToScan("com.company");
em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
em.afterPropertiesSet();
return em.getObject();
}
Also the JpaTransactionManager is miss-configured too. It should be something like:
#Bean(name = "readingTransactionManager")
public PlatformTransactionManager readingTransactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(readingEntityManagerFactory());
return transactionManager;
}
You need to do the same for both the reading and the writing EntityManager configurations.
I've just updated from Spring 3.1.1 to 3.2.6
With 3.1 the following code worked well:
#Bean(name = DEMO_DS)
public JndiObjectFactoryBean demoDataSource()
{
JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName(JDBC_DEMO_DS);
factory.setProxyInterface(DataSource.class);
return factory;
}
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory(#Qualifier(DEMO_DS) DataSource dataSource)
{
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}
However with the uprgraded version I get the following exception:
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [javax.sql.DataSource] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for
this dependency. Dependency annotations:
{#org.springframework.beans.factory.annotation.Qualifier(value=DemoDataSource)}
I have multiple DataSources hence the #Qualifier is a need.
Thanks.
Edit:
It seems that this solves the problem:
public DataSource dataSourceFactory() {
try
{
return (DataSource) demoDataSource().getObject();
}
catch (Exception ex)
{
throw new RuntimeException(ex);
}
}
...
sessionFactory.setDataSource(dataSourceFactory());
However I don't think it's a nice solution.
Depending on your need rewrite your configuration a little. If you don't really need the datasource injected you can do something like this.
#Bean(name = DEMO_DS)
public JndiObjectFactoryBean demoDataSource() {
JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName(JDBC_DEMO_DS);
factory.setProxyInterface(DataSource.class);
return factory;
}
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory() {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(demoDataSource().getObject());
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}
If you need to have a datasource injected you might want to switch to using a JndiLocatorDelegate to do the lookup instead of a JndiObjectFactoryBean.
#Bean(name = DEMO_DS)
public DataSource demoDataSource() throws NamingException {
return JndiLocatorDelegate.createDefaultResourceRefLocator().lookup(JDBC_DEMO_DS, DataSource.class);
}
This gives you a DataSource directly instead of a FactoryBean<Object> (which is what the JndiObjctFactoryBean is) what probably is the source of the problem.
Or (in theory) you should also be able to use a #Value annotation on a property in your config class. Instead of a #Value a normal #Resource should also do the trick (that can also delegate a call to JNDI for a lookup).
public class MyConfig {
#Value("${" + JDBC_DEMO_DS + "}")
private DataSource demoDs;
}
With #Resource
public class MyConfig {
#Resource(mappedName=JDBC_DEMO_DS)
private DataSource demoDs;
}
And you can then simply reference it in your configuration method.
#Bean(name = DEMO_SESSION_FACTORY)
public SqlSessionFactoryBean demoSqlSessionFactory() {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(demoDs);
sessionFactory.setConfigLocation(new ClassPathResource("demo/config.xml"));
return sessionFactory;
}