Spring transaction propogation on non spring managed beans - spring

I am working on a migration project which involves upgrading the platform to Spring 4 with MyBatis. In the legacy code, transactions are handled at a central locations wherein call to start/end transactions are spread across various classes like service class, helper class and DAO class.
I managed to convert all service classes to spring managed component and DAO classes to support MyBatis-spring API. Problem is my service class use several other classes to perform a function and those classes are all instantiated manually and used. Now if i start a transaction on service class methods and perform database transactions inside other helper or DAO classes which are not spring managed, my transaction handling doesn't work correctly. I have illustrated this problem in the below code. Could you tell what are the ways to acheive transaction handling without modifying the code?
Example :
package com.service;
#Service
class MyService {
#Transactional( propagation=Propagation.REQUIRED)
public void processRequest () {
HelperClass helper = new HelperClass();
helper.performOperation();
}
}
package com.helper;
// this class is not spring bean
class HelperClass {
// MyBatis mapper class
private EmployeeMapper mapper;
public HelperClass () {
mapper = // retrieve mapper class bean from spring context
}
public performOperation () {
// call to mapper class insert operation
// call to mapper class update operation
}
}
package com.dao;
#Component
interface EmployeeMapper {
// method definition to perform database operation
}
Spring configuration details:
<context:component-scan base-package="com" />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
....
....
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" />
<mybatis:scan base-package="com.dao" />
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations"
value="classpath*:mybatis/*.xml" />
</bean>
In the above code HelperClass.performOperation() method is doing 2 database operations (insert,update). Say if insert succeeds and update fails, my database transaction doesn't get rollback. Since I already started the transaction at MyService.processRequest() should this not rollback the operations that are carried inside that method call? Correct me if my understanding is wrong.

Related

Spring JPA transaction over multiple methods

I'm using Spring 3.2 with JPA and Hibernate 4 in a web application running in Tomcat 7. The application is divided into controller, service an DAO classes. The service classes have an annotated transaction configuration at class and method level. The DAOs are plain JPA with entity manager injected by #PersistenceContext annotation.
#Service("galleryService")
#Transactional(propagation=Propagation.SUPPORTS, readOnly=true)
public class GalleryServiceImpl implements GalleryService {
#Override
public Picture getPicture(Long pictureId) {
return pictureDao.find(pictureId);
}
#Override
public List<PictureComment> getComments(Picture picture) {
List<PictureComment> comments = commentDao.findVisibleByPicture(picture);
Collections.sort(comments, new Comment.ByCreatedOnComparator(Comment.ByCreatedOnComparator.SORT_DESCENDING));
return comments;
}
...
}
#Controller
#RequestMapping("/gallery/displayPicture.html")
public class DisplayPictureController extends AbstractGalleryController {
#RequestMapping(method = RequestMethod.GET)
public String doGet(ModelMap model, #RequestParam(REQUEST_PARAM_PICTURE_ID) Long pictureId) {
Picture picture = galleryService.getPicture(pictureId);
if (picture != null) {
model.addAttribute("picture", picture);
// Add comments
model.addAttribute("comments", galleryService.getComments(picture));
} else {
LOGGER.warn(MessageFormat.format("Picture {0} not found.", pictureId));
return ViewConstants.CONTENT_NOT_FOUND;
}
return ViewConstants.GALLERY_DISPLAY_PICTURE;
}
...
}
I switched on debug logging for org.springframework.transaction and noticed, that "Creating new transaction", "Opened new EntityManager", "Getting...", "Closing..." and "Committing transaction" is done for every call of a method in my service class. Even if those methods where called by one single method in my controller class. Here is an example of my log output:
2014-12-03 10:53:00,448 org.springframework.transaction.support.AbstractPlatformTransactionManager getTransaction
DEBUG: Creating new transaction with name [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly
2014-12-03 10:53:00,448 org.springframework.orm.jpa.JpaTransactionManager doBegin
DEBUG: Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#6133a72f] for JPA transaction
2014-12-03 10:53:00,468 org.springframework.orm.jpa.JpaTransactionManager doBegin
DEBUG: Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle#5182c1b7]
2014-12-03 10:53:00,468 org.springframework.transaction.interceptor.TransactionAspectSupport prepareTransactionInfo
TRACE: Getting transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]
2014-12-03 10:53:00,489 org.springframework.transaction.interceptor.TransactionAspectSupport commitTransactionAfterReturning
TRACE: Completing transaction for [de.domain.webapp.gallery.service.GalleryServiceImpl.getPicture]
2014-12-03 10:53:00,489 org.springframework.transaction.support.AbstractPlatformTransactionManager processCommit
DEBUG: Initiating transaction commit
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCommit
DEBUG: Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#6133a72f]
2014-12-03 10:53:00,489 org.springframework.orm.jpa.JpaTransactionManager doCleanupAfterCompletion
DEBUG: Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl#6133a72f] after transaction
2014-12-03 10:53:00,489 org.springframework.orm.jpa.EntityManagerFactoryUtils closeEntityManager
DEBUG: Closing JPA EntityManager
I know that I can use an OpenSessionInView to hold the hibernate session for a complete request but some people said, OSIV is an antipattern. Instead I'm using SpringOpenEntityManagerInViewFilter. But with no success. How can I achieve, Spring uses a single transaction for multiple service layer method calls of my controller? Or did I missunderstand anything?
Here a part of my Spring configuration:
<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" />
<property name="showSql" value="false" />
</bean>
</property>
</bean>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/tikron" />
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<bean id="jpaTemplate" class="org.springframework.orm.jpa.JpaTemplate">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan base-package="de.domain.webapp">
<context:include-filter type="regex" expression=".*Service"/>
</context:component-scan>
My persistence unit:
<persistence-unit name="tikron-data" transaction-type="RESOURCE_LOCAL">
<!-- Entities located in external project tikron-data -->
<jar-file>/WEB-INF/lib/tikron-data-2.0.1-SNAPSHOT.jar</jar-file>
<!-- Enable JPA 2 second level cache -->
<shared-cache-mode>ALL</shared-cache-mode>
<properties>
<property name="hibernate.hbm2ddl.auto" value="validate" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory"/>
<property name="hibernate.generate_statistics" value="false" />
</properties>
</persistence-unit>
Some more log output from application startup:
2014-12-03 10:46:48,428 org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean createNativeEntityManagerFactory
INFO: Building JPA container EntityManagerFactory for persistence unit 'tikron-data'
2014-12-03 10:46:48,428 org.hibernate.ejb.HibernatePersistence logDeprecation
WARN: HHH015016: Encountered a deprecated javax.persistence.spi.PersistenceProvider [org.hibernate.ejb.HibernatePersistence]; use [org.hibernate.jpa.HibernatePersistenceProvider] instead.
2014-12-03 10:46:48,448 org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
name: tikron-data
...]
...
2014-12-03 10:46:51,101 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory#6cccf90d: defining beans [propertyConfigurer,messageSource,entityManagerFactory,dataSource]; root of factory hierarchy
2014-12-03 10:46:51,111 org.springframework.web.context.ContextLoader initWebApplicationContext
INFO: Root WebApplicationContext: initialization completed in 3374 ms
Thanks in advance.
You need to play your PROPAGATION_LEVEL and structure your service bean calls properly. If you're using #Transactional on your Service classes, what you described is normal, since the transaction demarcation happens on public methods level. So depending on the propagation level when entering the public method of the service bean, the transaction will either start, join an existing transaction, throw an exception or execute non-transactionally.
To have service methods execute in one transaction, its enough to have the propagation level set to support as you do in your GalleryService (providing that you don't override it on a method level), and call these methods from a single method of another service which is annotated #Transactional(propagation=Propagation.REQUIRED). Its important to have you're calls pass through a bean e.g. (galleryService.getPicture instead of local call getPicture), 'cause aspects that inject the transaction semantics work against a proxy that wraps the bean
#Service("exampleService")
#Transactional(propagation=Propagation.REQUIRED)
public class ExampleServiceImpl implements ExampleService {
#Autowired
private GalleryService galleryService;
#Override
public void singleTransaction() {
galleryService.getPicture
galleryService.getComments
}
...
}
a brief PROPAGATION_LEVEL glossary
MANDATORY
Support a current transaction, throw an exception if none exists.
NESTED
Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.
NEVER
Execute non-transactionally, throw an exception if a transaction exists.
NOT_SUPPORTED
Execute non-transactionally, suspend the current transaction if one exists.
REQUIRED
Support a current transaction, create a new one if none exists.
REQUIRES_NEW
Create a new transaction, suspend the current transaction if one exists.
SUPPORTS
Support a current transaction, execute non-transactionally if none exists.
UPDATE with respect to the comment
But is combining service method calls into one service method the only way to handle those calls in one single transaction?
No, but in my opinion its your best option. Consider the article http://www.ibm.com/developerworks/java/library/j-ts2/index.html. What I'm describing is whats referred to in the article as API layer strategy. Its defined as
The API Layer transaction strategy is used when you have
coarse-grained methods that act as primary entry points to back-end
functionality. (Call them services if you would like.) In this
scenario, clients (be they Web-based, Web services based,
message-based, or even desktop) make a single call to the back end to
perform a particular request.
Now in the standard three-layer architecture you have the presentation, a business and a persistence layer. In simple words you can annotate your controllers, services or DAOs. Services are the ones holding the logical unit of work. If you annotate your controllers, they are part of your presentation layer, if your transactional semantics is there, and you decide to switch or add a non-http client (e.g. Swing client), you're bound to either migrate or duplicate your transaction logic. DAO layers should also not be the owners of transaction, 'the granularity of the DAO methods is much less than what is a business logical unit. I'm restraining from points like best-practice etc. but, if you're uncertain, choose your business (service) as your API transaction layer :)
You have numerous posts discussing this topic in all directions
why use #transactional with #service insted of with #controller
Where should "#Transactional" be place Service Layer or DAO
very good and fun reading, many opinions and very context-dependant

Spring + Hibernate How to use AspectJ to control transaction

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with #Transactional.
So I want to use AspectJ, but I can not get session by sessionFactory.getCurrentSession(), which leads to following Exception.
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I have added VM arguments at runtime.
-javaagent:/Users/xx/workStation/workspace/SpringHibernate/src/lib/spring-instrument-3.2.8.RELEASE.jar
Here is configuration file
aop.xml
<aspectj>
<weaver options="-Xset:weaveJavaxPackages=true -showWeaveInfo">
</weaver>
beans.xml
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref = "dataSource"/>
<property name="mappingResources">
<list>
<value>config/User.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<value>
hibernate.dialect=org.hibernate.dialect.MySQLDialect
hibernate.hbm2ddl.auto=update
hibernate.show_sql=true
hibernate.format_sql=false
</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref = "sessionFactory"/>
<property name="nestedTransactionAllowed" value = "true"/>
</bean>
<tx:annotation-driven mode="aspectj" transaction-manager="txManager" />
<context:load-time-weaver />
Thank you for your answers.
If I removed aop.xml I got a warning:
warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified.
else I got following info
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(java.util.List com.yonghui.sh.service.impl.UserServiceBean.getUsers())' in Type 'com.yonghui.sh.service.impl.UserServiceBean' (UserServiceBean.java:106) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
[AppClassLoader#546b97fd] weaveinfo Join point 'method-execution(void com.yonghui.sh.service.txtest.TxTestB.txC())' in Type 'com.yonghui.sh.service.txtest.TxTestB' (TxTestB.java:32) advised by around advice from 'org.springframework.transaction.aspectj.AnnotationTransactionAspect' (AbstractTransactionAspect.aj:59)
.
I think classes are weaved. I just use JUnit to run some tests not in web context.
It seems that the problem is "static".
static UserService userService;
static TxTest txTest;
static TxTestX txTestx;
#BeforeClass
public static void setUpBeforeClass() throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
userService = (UserService)ac.getBean("UserService");
txTest = (TxTest)ac.getBean("txTestA");
txTestx = (TxTestX)ac.getBean("txTestX");
}
If I don't use JUnit it works well.
public class TxTestMain {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
TxTest txTestA = (TxTest)ac.getBean("txTestA");
txTestA.txA();
}
}
How comes??
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional.
I dont know if it is a problem here But a bean can invoke its own method through proxy. Just implement in your bean interfaces like ApplicationContextAware and BeanNameAware and having this two you can ask the context about the bean where you actually are and cal any method through proxy (in transaction). This of course work only when the scope is SINGLETON if it is PROTOTYPE there are also some ways to do that through API.

Issue with #Transactional annotations in Spring JPA

I have a doubt related to transactions within transactions. For background, I have a School entity object which has Set of Students entity object mapped to it. I am using Spring Data JPA which is taking care of all the crud operations. I have a SchoolManagementService class which has #Transactional(readonly=true) set at the class level and for all updating methods I am using #Transactional over them.
In my SchoolManagementService class I have a method deleteStudents(List) which I have marked as #Transactional. In this method I am calling StudentsRepository.delete(studentId) again and again. I want to make sure if any delete fails then the transaction should rollback for that checked exception. I am trying to test this with my spring junit test case (I am not using default rollback=true or#rollback(true) because I want this to be rollbacked because of some runtime exception I encounter at the repository delete method.
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class})
#ContextConfiguration(locations = {"classpath:PPLRepository-context.xml"})
public class TestClass{
#Test
#Transactional
public void testDeleteStudents(){
StudentManagementService.delete(randomList)
}
with this testcase it is deleting all the records but the last one. Ideally it should rollback and none of of entries should be deleted.
Here is my sprin settings file with TransactionMangaer configs
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init"
destroy-method="close">
<property name="forceShutdown" value="true" />
<property name="startupTransactionService" value="true" />
<property name="transactionTimeout" value="1000" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" />
<!-- Configure the Spring framework to use JTA transactions from Atomikos -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
<property name="transactionSynchronizationName" value="SYNCHRONIZATION_ON_ACTUAL_TRANSACTION" />
</bean>
<!-- EntityManager Factory that brings together the persistence unit, datasource, and JPA Vendor -->
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="PPL_GMR">
<property name="dataSource" ref="PPL_GMRDS"></property>
<property name="persistenceUnitName" value="PPL_GMR"/>
<property name="persistenceXmlLocation" value="classpath:META-INF/PPL-persistence.xml"/>
<property name="jpaVendorAdapter" ref="PPL_GMRJPAVendorAdapter"/>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.transaction.manager_lookup_class" value="com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup"/>
<entry key="hibernate.connection.release_mode" value="on_close"/>
<entry key="hibernate.default_schema" value="${PPL.schema}"/>
</map>
</property>
</bean>
Can someone suggest where my understanding of transactions is wrong? Whatever I have read from the APIs I got this impression that if some method is #Transactional at the service layer and if it calls several #Transactional methods of Spring Data JPA repositories then if I encounter any Runtime exception then all the transactions should be rolled back.
I even tried to simple create a testcase method as below:
#Test
#Transactional
public void testDeleteStudents(){
StudentRepository.delete(1);
StudentRepository.delete(2);// 2 id is not present so I will get a runtime exception.
}
Inspite of keeping #Rollback(true/false) on this method, this method deletes id 1 Student from the database. I thought that #Transactional at this testcase method will create a new transaction here and all the transactional delete methods from the StudentRepository will run in same transaction. And no student data will be committed until and unless no runtime exception is thrown.
Please help me understand transactions better as I am new to this. i am using Spring Data JPA with Oracle database.
Thanks in advance.
I think that the default behaviour is (even thought you don't have it on the test class)
#TransactionConfiguration(defaultRollback = true)
so it will perform rollback when your test ends. Therefore there is no synchronization of hibernate session with the database and no queries SQL are issued to the database.
You have two posibilities. Either specify
#TransactionConfiguration(defaultRollback = false)
or inject entity manager into your test and call
#PersistenceContext
protected EntityManager em;
/**
* Simulates new transaction (empties Entity Manager cache).
*/
public void simulateNewTransaction() {
em.flush();
em.clear();
}
This will force hibernate to send all queries to the database. Please note that this will solve your problem with deleting non existing entity, but it doesn't behave exactly like new transaction, e.g. when you have missing foreign key it doesn't throw anything (this is predictable.
You can use this for checking the contents of entity returned by em.find(class, id) and check you relational mapping without the need to commit the transaction.

Injection of autowired dependencies failed while using #Transactional

I testing my DAO, but it didn't work. The following error occurs:
Tests in error:
testAccountOperations(com.tsekhan.rssreader.dao.HibernateControllerTest): Error creating bean with name 'com.tsekhan.rssreader.dao.HibernateControllerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: com.tsekhan.rssreader.dao.HibernateController com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController; nested exception is java.lang.IllegalArgumentException: Can not set com.tsekhan.rssreader.dao.HibernateController field com.tsekhan.rssreader.dao.HibernateControllerTest.hibernateController to $Proxy25
My DAO:
#Service
#Scope("singleton")
public class HibernateController extends HibernateDaoSupport {
#Autowired
public SessionFactory sessionFactory;
#Transactional
public void addAcount(Account account) {
sessionFactory.getCurrentSession().saveOrUpdate(account);
}
}
My test for this DAO:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("classpath:/applicationContext.xml")
public class HibernateControllerTest {
#Autowired
HibernateController hibernateController;
private Set<Channel> getTestChannelList(String channelLink) {
Channel testChannel = new Channel();
testChannel.setSourceLink(channelLink);
Set<Channel> testChannelList = new HashSet<Channel>();
testChannelList.add(testChannel);
return testChannelList;
}
private Account getTestAccount(String accountLogin, String channelLink) {
Account testAccount = new Account();
testAccount.setAccountLogin(accountLogin);
testAccount.setChannelList(getTestChannelList(channelLink));
return testAccount;
}
#Test
public void testAccountOperations() {
hibernateController
.addAcount(getTestAccount("test_login", "test_link"));
}
}
My applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd"
default-autowire="byName">
<!-- Enabling spring-transaction annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- Enabling annotation-driven configurating -->
<context:annotation-config />
<!-- Creation of transaction manager -->
<bean id="transactionManager" scope="singleton"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="sessionFactory" scope="singleton"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="configLocation" value="classpath:/hibernate.cfg.xml"/>
<property name="configurationClass">
<value>org.hibernate.cfg.AnnotationConfiguration</value>
</property>
</bean>
<!--
A Spring interceptor that takes care of Hibernate session lifecycle.
-->
<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterceptor">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean name="employeeDAO" scope="prototype"
class="com.tsekhan.rssreader.dao.HibernateController" />
<!-- Searching for hibernate POJO files in package com.tsekhan.rssreader.web -->
<context:component-scan base-package="com.tsekhan.rssreader.web" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />
</beans>
I note, that if you comment #Transactional in DAO, bean is created correctly. What happens?
First of all its realy bad to give name ending in Controller to a DAO its very confusing, Controller and DAO have all together different purpose.
When you add #Transactional to a service or dao class, for spring to make it work in a transaction needs to create a proxy of that class, its a kind of wrapper where in before the execution of proxied class(class in consideration which is proxied) method spring starts the transaction and after the execution in case no exceptions completes the transaction, this can be done in spring via AOP and Annotations. To describe in code.
public class OriginalDaoImpl implements OriginalDao extends DaoSupport {
public void save(Object o){
manager.save(o);
}
}
public class ProxyDaoImpl implements OriginalDao {
private OriginalDao originalDaoImpl; //instance of OriginalDaoImpl
public void save(Object o){
try{
transaction.start();
originalDaoImpl.save(o);
transaction.commit();
}catch(Exception e){
transaction.rollback();
}finally{
//clean up code
}
}
}
As you see this is not an exact implementation but a foundation code, how transaction magically works for you. The key point is there interface OriginalDao which makes this injection easy as OriginalDaoImpl and ProxyDaoImpl both implement same interface. Hence they can be swapped i.e. proxy taking place of original. This dynamic proxy can be created in java by Java dynamic proxy. Now, the question what if your class is not implementing an interface, it gets harder for the replacement to happen.
One of the libraries CGLIB as far as I know, helps in such a scenario, whereby it generates a dynamic subclass for the class in consideration and in overriden method performs the magic as described above, by calling super.save(o) to delegate to original code.
Now to the problem of injection.
Create interface and make your dao implement that and spring will default to JDK proxy as it is behaving now.
Add proxy-target-class="true" attribute to <tx:annotation-driven transaction-manager="transactionManager"/>
As far as exception is concerned it is throwing as it is expecting injected bean to be of type 'HibernateController' but its not.
For you reference you can refer links below.
10.5.6 Using #Transactional
Spring AOP Doc
Hope this helps !!!!!.
If your are using Spring MVC make sure to scan specific controller classes alone in servlet context file. Otherwise it will scan 2 times and transaction is not available on the application context.

Ability to switch Persistence Unit dynamically within the application (JPA)

My application data access layer is built using Spring and EclipseLink and I am currently trying to implement the following feature - Ability to switch the current/active persistence unit dynamically for a user. I tried various options and finally ended up doing the following.
In the persistence.xml, declare multiple PUs. Create a class with as many EntityManagerFactory attributes as there are PUs defined. This will act as a factory and return the appropriate EntityManager based on my logic
public class MyEntityManagerFactory {
#PersistenceUnit(unitName="PU_1")
private EntityManagerFactory emf1;
#PersistenceUnit(unitName="PU_2")
private EntityManagerFactory emf2;
public EntityManager getEntityManager(int releaseId) {
// Logic goes here to return the appropriate entityManeger
}
}
My spring-beans xml looks like this..
<!-- First persistence unit -->
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emFactory1">
<property name="persistenceUnitName" value="PU_1" />
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager" id="transactionManager1">
<property name="entityManagerFactory" ref="emFactory1"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager1"/>
The above section is repeated for the second PU (with names like emFactory2, transactionManager2 etc).
I am a JPA newbie and I know that this is not the best solution. I appreciate any assistance in implementing this requirement in a better/elegant way!
Thanks!
First of all thanks to user332768 and bert. I tried using AbstractRoutingDataSource as mentioned in the link provided by bert, but got lost trying to hook up my jpa layer (eclipselink). I reverted to my older approach with some modifications. The solution looks cleaner (IMHO) and is working fine. (switching database at runtime and also writing to multiple databases in the same transaction)
public class MyEntityManagerFactoryImpl implements MyEntityManagerFactory, ApplicationContextAware {
private HashMap<String, EntityManagerFactory> emFactoryMap;
public EntityManager getEntityManager(String releaseId) {
return SharedEntityManagerCreator.createSharedEntityManager(emFactoryMap.get(releaseName));
}
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
Map<String, LocalContainerEntityManagerFactoryBean> emMap = applicationContext.getBeansOfType(LocalContainerEntityManagerFactoryBean.class);
Set<String> keys = emMap.keySet();
EntityManagerFactory entityManagerFactory = null;
String releaseId = null;
emFactoryMap = new HashMap<String, EntityManagerFactory>();
for (String key:keys) {
releaseId = key.split("_")[1];
entityManagerFactory = emMap.get(key).getObject();
emFactoryMap.put(releaseId, entityManagerFactory);
}
}
}
I now inject my DAO's with an instance (singleton) of MyEntityManagerFactoryImpl. The dao will then simply call createSharedEntityManager with the required release and will get the correct EntityManager for that database. (Note that i am now using application managed EntityManager and hence i have to explicitly close them in my dao)
I also moved to jta transaction manager (to manage transaction across multiple databases)
This is how my spring xml looks like now.
...
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel1">
<property name="persistenceUnitName" value="PU1" />
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="em_Rel2">
<property name="persistenceUnitName" value="PU2" />
</bean>
<bean class="org.springframework.transaction.jta.JtaTransactionManager" id="jtaTransactionManager">
</bean>
<tx:annotation-driven transaction-manager="jtaTransactionManager"/>
....
Cheers! (comments are welcome)
I am not sure if this is a clean method. Instead of declaring the enitiymanagerfactory multiple times, we can use the spring application context to get the entitymanagerfactory declared in the spring application.xml.
hm = applicationContext.getBeansOfType(org.springframework.orm.jpa.LocalEntityManagerFactoryBean.class);
EntityManagerFactory emf = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf1")).getNativeEntityManagerFactory();
EntityManagerFactory emf2 = ((org.springframework.orm.jpa.LocalEntityManagerFactoryBean) hm.get("&emf2")).getNativeEntityManagerFactory();
This is something i need to do in the future too, for this i have bookmarked Spring DynamicDatasourceRouting
http://blog.springsource.com/2007/01/23/dynamic-datasource-routing/
As far as i understand, this is using one PU, which gets assigned different DataSources. Perhaps it is helpful.

Resources