I'm new to Spring's transaction management having troubles to tackle the following scenario of nested transactions while integrating Spring (3.2) and Hibernate (3.6).
I've declared two appContext files as following.
File1) applicationContext-student.xml
<bean id="studentProjSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
...**dataSource1_on_Machine1**...
</bean>
<bean id="studentProjTransactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="studentProjSessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="studentProjTransactionManager" />
<bean id="studentDao" class="com.my.univ.employee.dao.studentHibDao" scope="singleton" />
<bean id="studentService" class="com.my.univ.student.service.studentServiceImpl" scope="singleton">
<property name="studentDao" ref="studentDao" />
</bean>
File2) applicationContext-employee.xml
<bean id="employeeProjSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
........**dataSource2_on_Machine2**...
</bean>
<bean id="employeeProjTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="employeeProjSessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="employeeProjTransactionManager" />
<bean id="employeeDao" class="com.my.univ.employee.dao.EmployeeHibDao" scope="singleton" />
<bean id="employeeService" class="com.my.univ.employee.service.EmployeeServiceImpl" scope="singleton">
<property name="employeeDao" ref="employeeDao" />
</bean>
Imported above two files in the following file.
File3) applicationContext-university.xml
<import resource="applicationContext-student.xml" />
<import resource="applicationContext-employee.xml" />
<bean id="personService" class="com.my.univ.person.service.PersonServiceImpl" scope="singleton">
<property name="studentService" ref="studentService" />
<property name="employeeService" ref="employeeService" />
</bean>
Questions
Let's assume that method level #Transactional annotations are provided with the right txManager names in studentService and employeeService but not in personService.
Q1) If I declare a method in personService as #Transactional, which txManager gets picked?
Q2) How does the nested txManager scenario work if the txManagers in the hierarchy are different from each other?
Ex: If a #Transactional method in personService invokes a #Transactional method in studentService and then another #Transactional method in employeeService (with in the same method of personService).
Q3) How does the commit, rollback elements work in above scenario.
Q4) Readonly operations vs Read/Write operations in above scenario.
It'd be great if anybody could clarify the above.
Thanks.
The #Transactional annotation takes as a value the bean name of the transaction manager. From the documentation:
public abstract String value
A qualifier value for the specified transaction.
May be used to determine the target transaction manager, matching the qualifier value (or the bean name) of a specific PlatformTransactionManager bean definition.
Default:
""
So in your case, explicitly defining:
#Transactional("studentProjTransactionManager")
#Transactional("employeeProjTransactionManager")
should wrap transactionally using the right transaction manager.
Related
Im using JPA with Hibernate implementation and using JpaTransactionManager to mange transactions.
Below is my application context file
<bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="defaultDataSource" ref="dataSource" />
</bean>
<bean id="entityManagerFactory" primary="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitManager" ref="persistenceUnitManager" />
<property name="persistenceUnitName" value="infra_services" />
</bean>
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="transactionManager"
proxy-target-class="true" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
</property>
</bean>
I have defined my service class as below
#Service
#Transactional
public class ComponentService {
I execute queries in dao layer as below
Query q = entityManager.createQuery(
"SELECT cc.component FROM "
+ this.typeParameterClass.getSimpleName()
+ " cc WHERE cc.caseload.id = ? ").setParameter(1,
caseloadId);
Collection<Component> ddd =q.getResultList();
for (Component c : ddd) {
System.out.println(c.getComponentId());
System.out.println(c.getComponentRelationships2());
}
return ddd;
I started with select queries. While executing the line System.out.println(c.getComponentRelationships2()); getting could not initialize proxy - no Session] with root cause exception
Not sure why the session is not available here. Please help me on this.
If your service is not in the same context as the one where <tx:annotation-driven /> then it's not working. Because it only look for bean in the same context. Extract from spring doc:
#EnableTransactionManagement and only looks for #Transactional on beans in the same application context they are defined in. This means that, if you put annotation driven configuration in a WebApplicationContext for a DispatcherServlet, it only checks for #Transactional beans in your controllers, and not your services. See Section 21.2, “The DispatcherServlet” for more information.
Have the following configuration in a java project-
<bean id="testTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="testDataSource" />
</bean>
<bean class="org.springframework.transaction.aspectj.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="testTxManager" />
</bean>
But in the DOA java class, #transactional is not used over any of the methods.
Will Transaction management be still applicable on these methods?
I am using Spring 3 and Hibernate 4
I have the following in DAO class
public void create(PersistEmployeee employee){
entityManager.persist(employee);
}
However nothing is saved in database.
I have tried adding #Transactional to method create, didn't work, added #Transactional in my DAO class, didn't work either. I have #Transactional(readOnly = false) in EmployeeServiceImpl class, but that didn't help.
I have the following in applicationContext.xml
<tx:annotation-driven transaction-manager="txManager" />
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="txManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="SessionFactory" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
How can I resolve this issue?
Why do you have two transaction managers? I'm not sure about the effects of duplicating the annotation-driven element, but I guess it's asking for trouble.
According to its JavaDoc, PersistenceAnnotationBeanPostProcessor seems to be responsible for injecting the EntityManager with the annotation #PersistenceContext. It appears to imply without this bean declared in the Spring application context xml, the #PersistenceContext annotation won't work.
However, based on my experiments, this is not the truth.
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_1_0.xsd"
version="1.0">
<persistence-unit name="default" transaction-type="RESOURCE_LOCAL" />
</persistence>
Spring application context XML
<context:component-scan base-package="com.test.dao" />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceUnitName" value="default"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.DerbyDialect"/>
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver"/>
<property name="url" value="jdbc:derby://localhost:1527/c:\derbydb\mydb"/>
<property name="username" value="APP"/>
<property name="password" value="APP"/>
</bean>
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<!--
<bean id="persistenceAnnotation" class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
-->
UserDaoImpl
#Repository("userDao")
public class UserDaoImpl implements UserDao {
#PersistenceContext
protected EntityManager entityManager;
#Transactional
public void save(User user) {
entityManager.persist(user);
}
}
Whether I comment or uncomment the persistenceAnnotation bean, the result is the same. It doesn't hurt to leave the bean around, but what's the use of this bean?
I am using Spring 3.0.5.
Could someone provide a scenario where taking out this bean will result in failure?
Also I am not fond of creating an empty persistence unit just to fool Spring. Luckily this problem has been addressed in Spring 3.1.0.
The PersistenceAnnotationBeanPostProcessor transparently activated by the <context:component-scan /> element. To be precise it's the <context:annotation-config /> element that activates the bean but this element in turn gets transparently activated by <context:component-scan />.
As Oliver Gierke mentioned, org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor is automatically loaded into App Context by Spring when using annotation based configuration. One of its duties is to search the proper entity EntityManagerFactory that would provide the EntityManager for you #PersistenceContext annotated properties.
If you have multiple EntityManagerFactory beans in you spring config/context and you have #PersistenceContext annotations without a unitName attribute (lets say you are using a framework that comes with such a bean, and you can't touch framework code), you may run into this exception: org.springframework.beans.factory.NoUniqueBeanDefinitionException.
I found this workaround in case you tun into this:
<bean id="org.springframework.context.annotation.internalPersistenceAnnotationProcessor"
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" >
<property name="defaultPersistenceUnitName" value="entityManagerFactory"/>
</bean>
This would override the default PersistenceAnnotationBeanPostProcessor loaded by Spring with a new one with defaultPersistenceUnitName.
I'm developing a web application using Struts2 + Spring, and now I'm trying to add a scheduled task. I'm using Spring's task scheduling to do so. In my applicationContext I have:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
...
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL" />
</bean>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
And then I have my DAO that uses this entityManagerFactory:
<bean id="dao" class="data.GenericDAO" />
So this works flawlessly within the web application. But now I have a problem when creating the scheduled task:
<task:scheduled-tasks scheduler="notifier">
<task:scheduled ref="emailService" method="sendMail" fixed-rate="30000" />
</task:scheduled-tasks>
<task:scheduler id="notifier" pool-size="10" />
<bean id="emailService" class="services.emailService" >
<property name="dao" ref="dao" />
</bean>
This executes the method sendMail on my emailService class every 30 seconds. And my emailService has the DAO injected correctly. The thing is that I can fetch objects with my DAO using the findById named queries, but when I try to access any property mapped by Hibernate, such as related collections or entities, I get an "LazyInitializationException: could not initialize proxy - no Session ". I don't know what's wrong, since I believe the scheduled task is being managed by Spring, so it should have no problem using a Spring managed DAO. I must say that I'm using the openSessionInView filter on my struts actions, so maybe I need something similar for this scheduled task.
Any help or suggestion will be appreciated, thanks!
Edit: Finally I found a way to fix this. I changed my regular Dao with one where I can decide when to start and commit the transaction. So before doing anything I start a transaction and then everything works OK. So I still don't know exactly what causes the problem and if someday I'll be able to use my regular DAO, for the moment I'm staying with this solution.
OpenSessionInView won't help you, because you don't have a web context. You need Spring's Declarative Transaction Management.
In most cases, what you need to do is just this XML:
<!-- JPA, not hibernate -->
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="myTxManager" />
<!-- without backing interfaces you probably also need this: -->
<aop:config proxy-target-class="true">
(Annotate your EmailService class as #Transactional to enable this)