Spring MVC + Hibernate: could not initialize proxy - no Session - spring

Note: See my own answer to this question for an example of how I solved this issue.
I am getting the following exception in my Spring MVC 4 + Hibernate 4 project:
org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: com.mysite.Company.acknowledgements, could not
initialize proxy - no Session
After reading a lot of other questions about this issue, I understand why this exception occurs, but I am not sure how to fix it in a good way. I am doing the following:
My Spring MVC controller calls a method in a service
The service method invokes a method in a DAO class
The method in the DAO class fetches an entity object through Hibernate and returns it to the calling service
The service returns the fetched object to the controller
The controller passes the object to the view (JSP)
The view tries to iterate on a many-to-many association that is lazily loaded (and is thus a proxy object)
The exception is thrown because the session is closed at this point, and the proxy cannot load the associated data
I have previously worked with PHP and doctrine2, and this way of doing things caused no problems. I am trying to figure out the best way to solve this, because the solutions that I found so far don't seem so great:
Load the association eagerly, potentially loading lots of unnecessary data
Call Hibernate.initialize(myObject.getAssociation()); - this means that I have to loop through associations to initialize them (I guess), and just the fact that I have to do this, kind of makes the lazy loading less neat
Using a Spring filter to leave the session open in the views, but I doubt that this is a good thing to do?
I tried to use #Transactional in my service, but without luck. This makes sense because I am trying to access data that has not yet been loaded after my service method returns. Ideally I would like to be able to access any association from within my view. I guess the drawback of initializing the associations within my service is that I have to explicitly define which data I need - but this depends on the context (controller) in which the service is used. I am not sure if I can do this within my controller instead without losing the abstraction that the DBAL layer provides. I hope that makes sense. Either way, it would be great if I didn't have to always explicitly define which data I want to be available to my view, but just let the view do its thing. If that is not possible, then I am just looking for the most elegant solution to the problem.
Below is my code.
View
<%# page contentType="text/html;charset=UTF-8" language="java" %>
<%# taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<h1><c:out value="${company.name}" /> (ID: <c:out value="${company.id}" />)</h1>
<c:forEach var="acknowledgement" items="${company.acknowledgements}">
<p><c:out value="${acknowledgement.name}" /></p>
</c:forEach>
Controller
#Controller
public class ProfileController {
#Autowired
private CompanyService companyService;
#RequestMapping("/profile/view/{id}")
public String view(Model model, #PathVariable int id) {
Company company = this.companyService.get(id);
model.addAttribute("company", company);
return "viewCompanyProfile";
}
}
Service
#Service
public class CompanyServiceImpl implements CompanyService {
#Autowired
private CompanyDao companyDao;
#Override
public Company get(int id) {
return this.companyDao.get(id);
}
}
DAO
#Repository
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#Autowired
private SessionFactory sessionFactory;
#Override
public Company get(int id) {
return (Company) this.sessionFactory.getCurrentSession().get(Company.class, id);
}
}
Company entity
#Entity
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// Other fields here
#ManyToMany
#JoinTable(name = "company_acknowledgement", joinColumns = #JoinColumn(name = "company_id"), inverseJoinColumns = #JoinColumn(name = "acknowledgement_id"))
private Set<Acknowledgement> acknowledgements;
public Set<Acknowledgement> getAcknowledgements() {
return acknowledgements;
}
public void setAcknowledgements(Set<Acknowledgement> acknowledgements) {
this.acknowledgements = acknowledgements;
}
// Other getters and setters here
}
Acknowledgement entity
#Entity
public class Acknowledgement {
#Id
private int id;
// Other fields + getters and setters here
}
mvc-dispatcher-servlet.xml (a part of it)
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan">
<list>
<value>com.mysite.company.entity</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven />
Thanks in advance!

As #Predrag Maric said writing service methods with different initialization for entity assotiations might be an option.
OpenSessionInView is a quit discussable pattern, which can be a solution in your case, though.
Another option is to set
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
property. It is designed to solve org.hibernate.LazyInitializationException problem and is available since hibernate 4.1.6.
So what does this property do? It simply signals Hibernate that it
should open a new session if session which is set inside current
un-initialised proxy is closed. You should be aware that if you have
any other open session which is also used to manage current
transaction, this newly opened session will be different and it may
not participate into the current transaction unless it is JTA. Because
of this TX side effect you should be careful against possible side
effects in your system.
Find more information here: http://blog.harezmi.com.tr/hibernates-new-feature-for-overcoming-frustrating-lazyinitializationexceptions and
Solve Hibernate Lazy-Init issue with hibernate.enable_lazy_load_no_trans

The simplest and most transparent solution is the OSIV pattern. As I'm sure you aware, there is plenty of discussion about this (anti)pattern and the alternatives both on this site and elsewhere so no need to go over that again. For example:
Why is Hibernate Open Session in View considered a bad practice?
However, in my opinion, not all criticisms of OSIV are entirely accurate (e.g. the transaction will not commit till your view is rendered? Really? If you are using the Spring implementation then https://stackoverflow.com/a/10664815/1356423)
Also, be aware that JPA 2.1 introduced the concept of Fetch Graphs which give you more control over what is loaded. I haven't tried it yet but maybe this if finally a solution for this long standing problem!
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html

For others who are looking for a solution, here is how I solved the problem.
As Alan Hay pointed out, JPA 2.1+ supports entity graphs, which ended up solving my problem. To make use of it, I change my project to use the javax.persistence.EntityManager class instead of the SessionFactory, which would then hand me the current session. Here is how I configured it in my dispatcher servlet configuration (some things excluded):
<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:mvc="http://www.springframework.org/schema/mvc"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
<jee:jndi-lookup id="dataSource" jndi-name="java:/comp/env/jdbc/postgres" expected-type="javax.sql.DataSource"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="dk.better.company.entity, dk.better.user.entity" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQL9Dialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven />
</beans>
Below is an example of a DAO class.
#Transactional
public class CompanyDaoImpl implements CompanyDao {
#PersistenceContext
private EntityManager entityManager;
#Override
public Company get(int id) {
EntityGraph<Company> entityGraph = this.entityManager.createEntityGraph(Company.class);
entityGraph.addAttributeNodes("acknowledgements");
Map<String, Object> hints = new HashMap<String, Object>();
hints.put("javax.persistence.loadgraph", entityGraph);
return this.entityManager.find(Company.class, id, hints);
}
}
I found that if I did not use #PersistenceContext but Autowired instead, then the database session was not closed when rendering the view, and data updates were not reflected in subsequent queries.
For more information on entity graphs, I encourage you to read the following articles:
http://www.thoughts-on-java.org/2014/03/jpa-21-entity-graph-part-1-named-entity.html
http://www.thoughts-on-java.org/2014/04/jpa-21-entity-graph-part-2-define.html

My vote goes to Hibernate.initialize(myObject.getAssociation()) in service layer (which also means #Transactional should be moved from DAO to service methods)
IMHO, service methods should return all data that is required by the caller. If the caller wants to display some association on the view, it is service's responsibility to supply that data. This means you could have several methods that apparently do the same thing (return Company instance), but depending on the caller, different associations could be fetched.
On one project we had kind of configuration class, which contained information on what associations should be fetched, and we had a single service method which also accepted that class as a parameter. This approach meant we have only one method which is flexible enough to support all callers.
#Override
public Company get(int id, FetchConfig fc) {
Company result = this.companyDao.get(id);
if (fc.isFetchAssociation1()) {
Hibernate.initialize(result.getAssociation1());
}
...
return result;
}

Trigger in the service layer requires lazy loading of the Set's size method.
Tools:
public class HibernateUtil {
/**
* Lazy = true when the trigger size method is equal to lazy = false (load all attached)
*/
public static void triggerSize(Collection collection) {
if (collection != null) {
collection.size();
}
}
}
in your service method:
Apple apple = appleDao.findById('xxx');
HibernateUtil.triggerSize(apple.getColorSet());
return apple;
then use apple in controller, everything is ok!

Related

LazyInitializationException and #Transactional not working

I'm using Spring 3.2.9 with multilayered architecture design broken down to 3 modules web, service, repository. In repository i defined a generic DAO class which i inherit by other entity specific DAO classes.
The problem is that when i try to lazy fetch collection of my entity from service layer, I always get LazyInitializationException. I have tried putting #Transactional on my service class, but It appears to not work. I can only avoid exception if I initialize all lazy collections right away in the DAO class method (and only if i annotate DAO class with #Transactional), but I want to fetch those collections only when they are needed in bussiness logic, and not all in advance.
Strange thing is that #Transactional works only in DAO layer, but not in Service layer, where it should be used. I found a couple of ways to work around this issue, but I am interested to really underestend and solve this issue, and not only make make code work.
repository MODULE:
#Repository
public abstract class GenericDao<T> {
protected Class<T> entityClass;
#PersistenceContext
protected EntityManager entityManager;
.........
public T findById(long id) {
T entity = entityManager.find(entityClass, id);
if (entity == null) {
throw new EntityNotFoundException(entityClass.getSimpleName(), id);
}
return entity;
}
}
My service class in service MODULE:
#Service
public class UserServiceImpl implements UserService {
private UserDao userDao;
#SuppressWarnings("SpringJavaAutowiringInspection")
#Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
#Transactional(readOnly = true)
#Override
public UserDto getUserById(long id) {
User user = userDao.findById(id);
return new UserDto(user);
}
The DTO constructor tries to access user.getTeams() and then exception occurs. Instead of that, collection should be fetched with additional query to DB.
CONFIGURATION:
repository configuration:
......some other configurations like datasource...
<!--EntityManagerFactory-->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="persistenceXmlLocation" value="classpath:./META-INF/persistence.xml"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
</property>
<property name="jpaPropertyMap">
<map>
<entry key="hibernate.dialect" value="${hibernate_dialect}"/>
<entry key="hibernate.hbm2ddl.auto" value="${hibernate_Hbm2ddlAuto}"/>
<entry key="hibernate.show_sql" value="${hibernate_showSql}"/>
<entry key="hibernate.format_sql" value="${hibernate_formatSql}"/>
</map>
</property>
</bean>
persistence.xml:
<persistence-unit name="persistenceUnit">
...other classes..
<class>com.example.entity.User</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
</properties>
</persistence-unit>
service configuration:
<import resource="classpath*:META-INF/repositoryApplicationContext.xml"/>
<tx:annotation-driven/>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
It looks like #OneToMany association between the user and his or her teams which is lazy loaded. When you assign them to Dto it is still proxy not real collection. Put a break point there and see it in debug mode. Keep in mind any request (get a team or its size make it alive). One way is to fetch them in query, but
entityManager.find(entityClass, id)
doesn't have option for it. You may use
Hibernate.initialize(user.getTeams())
Found the answer after few days of headache.
I had to move:
<import resource="classpath*:META-INF/repositoryApplicationContext.xml"/>
<tx:annotation-driven/>
from repositoryConfiguration.xml to dispatcher-servlet.xml since that is the parent Spring context.
Thanks for the help.

Spring transaction propogation on non spring managed beans

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.

Spring Data JPA repositories, transactions and eventing in Spring

The amount of grey hair has dramatically increased in last couple of days while trying to resolve the following problem. I'm using Spring Data JPA repositories in custom event listeners that utilises simple Spring 3.2 eventing mechanism. The problem I'm having is that if ListenerA creates an entity and calls assetRepository.save(entity) or assetRepository.saveAndFlash(entity) the subsequent calls to retrieve this same entity from another listener fails. The cause seems to be that the ListenerB can not find the original entity in the database, it seem to be still in Hibernate's cache.
The trigger for ListenerB to lock up the entity is an event fired as a result of a runnable task execution from a thread pool.
Here is my configuration:
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="spring-jpa" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="generateDdl" value="false" />
<property name="database" value="#{appProps.database}" />
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</prop>
<prop key="hibernate.hbm2ddl.auto">#{appProps['hibernate.hbm2ddl.auto']}</prop>
<prop key="hibernate.show_sql">#{appProps['hibernate.show_sql']}</prop>
<prop key="hibernate.format_sql">#{appProps['hibernate.format_sql']}</prop>
<prop key="hibernate.search.default.directory_provider">org.hibernate.search.store.impl.FSDirectoryProvider</prop>
<prop key="hibernate.search.default.indexBase">#{appProps.indexLocation}</prop>
<prop key="hibernate.search.lucene_version">#{appProps['hibernate.search.lucene_version']}</prop>
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<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'm omitting the dataSource configuration which is an instance of ComboPooledDataSource that defines connection to Oracle database. As a side note, component scanning is used and the project is Spring MVC.
Now Java classes.
ListenerA
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private AssetRepository assetRepository;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
#Transactional
public void onApplicationEvent(FileUploadedEvent event) {
Asset target = event.getTarget();
Job job = new Job(target);
assetRepository.save(job);
executor.execute(job);
}
ListenerB
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private AssetRepository assetRepository;
#Override
#Transactional
public void onApplicationEvent(JobStartedEvent event) {
String id = event.getJobId();
Job job = assetRepository.findOne(id); // at this point we can not find the job, returns null
job.setStartTime(new DateTime());
job.setStatus(Status.PROCESSING);
assetRepository.save(job);
}
JobStartedEvent is fired from a runnable task within TaskExecutor.
What I'm doing wrong here? I have tried to use custom event publisher that is transaction aware, but that doesn't seem to solve the problem. I have also tried to wire appropriate service instead of data repository and remove #Transactional annotations from listeners, which also have failed. Any reasonable suggestions of how to solve the problem would be welcome.
I have managed to resolve the problem thanks to the hint from #Kresimir Nesek. So the solution was to replace Spring Data repositories with appropriate services.
Here are modified classes.
Listener A
#Sevice
public class ListenerA implements ApplicationListener<FileUploadedEvent> {
#Autowired
private JobService service;
#Autowired
private ExecutorService executor; // Triggers runnable task on a Job in Spring's TaskExecutor
#Override
public void onApplicationEvent(FileUploadedEvent event) {
Job job = service.initJobForExecution(event.getTarget());
executor.execute(job);
}
}
In the JobService method initJobForExecution(Asset target) had to be annotated with #Transactional(propagation=Propagation.REQUIRES_NEW) for everything to work.
Listener B
#Sevice
public class ListenerB implements ApplicationListener<JobStartedEvent> {
#Autowired
private JobService service;
#Override
public void onApplicationEvent(JobStartedEvent event) {
service.updateStatus(event.getJobId(), Status.PROCESSING);
}
}
While this is slightly old, I ran in to this same problem but now with Spring 4.1.1.RELEASE, Spring Data JPA 1.7.0 and Hibernate 4.3.5.Final
My scenario occurred during testing with some tests failing. During testing, our problems were caused by H2 in single connection mode, broadcasting asynchronous events and event transactionality.
Solutions
First problem was due to transaction timeout and was solved by adding MVCC=true to the H2 URL string. See: https://stackoverflow.com/a/6357183/941187
Asynchronous Events were causing issues during tests since they executed on different threads. In the event configuration, a task executor and thread pool were used. To fix, just provided an overridden configuration bean using the SyncTaskExecutor as the task executor. This will cause all events to occur synchronously.
The event transactionality was tricky. In our framework, event get broadcast from within a transaction (#Transactional). The event was then handled on another thread outside of the transaction context. This introduced a race condition since the handler often depended on the object from the transaction to have had been committed. We didn't notice the problem on our Windows development machines but it became apparent when deployed to production on Linux. The solution uses TransactionSynchronizationManager.registerSynchronization() with an implementation of TransactionSynchronization.afterCommit() to broadcast the event after committing. See http://www.javacodegeeks.com/2013/04/synchronizing-transactions-with-asynchronous-events-in-spring.html for more info and examples.
Related to #3, we had to add #Transactional(propagation = REQUIRES_NEW) for some of the service methods called from some of the event handlers.
Hopefully this helps some late comers.

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