PDA

View Full Version : Is @Transactional supported for @Controller classes?



AlexSavitsky
Sep 14th, 2009, 01:27 PM
I'm using a pretty standard setup - Tomcat, Spring 2.5, Java 6, JPA (Hibernate). Let's say I want to skip all the usual layered architecture, and use my EntityManager directly in my @Controller. However, I found out that the TransactionInterceptor isn't even called for @Controller invocations, leaving me without any transaction support, whether I declare @Transactional or not (and whether I declare it at the class or method level, too).

So I start to wonder, is it even supported?

Here's the code:

Domain Model (a simple 1-M relationship, DomainObject is a parent class that declares an ID, nothing more):


@Entity
public class Service extends DomainObject {
private String name;
@OneToMany(mappedBy = "service")
@MapKey(name = "key")
private Map<String, ServiceFee> fees = new HashMap<String, ServiceFee>();
}
@Entity
public class ServiceFee extends DomainObject {
private String key;
private Double amount;
@ManyToOne
private Service service;
}



Controller (TestForm is a Java Bean with a single property of type Service):


@Controller
@SessionAttributes(types = TestForm.class)
public class TestController {
@PersistenceContext
private EntityManager em;
@Transactional
@RequestMapping(value = "/show.form", method = RequestMethod.GET)
public String prepareForm(@RequestParam(value = "id", required = false) final Long serviceId, final Model model) {
TestForm form = new TestForm();
form.setService(em.find(Service.class, serviceId));
Hibernate.initialize(form.getService().getFees());
return "service";
}
public void setEm(EntityManager em) {
this.em = em;
}
}


XML config:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" />
<aop:aspectj-autoproxy />
<bean class="org.springframework.orm.jpa.support.PersistenceAnn otationBeanPostProcessor" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerD ataSource">
<property name="driverClassName" value="environment-specific" />
<property name="url" value="environment-specific" />
<property name="username" value="environment-specific" />
<property name="password" value="environment-specific" />
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityMa nagerFactoryBean"
p:dataSource-ref="dataSource" p:jpaVendorAdapter-ref="jpaAdapter" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory" />
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVen dorAdapter"
p:database="SQL_SERVER" p:showSql="false" />
<context:component-scan base-package="controller package" />
</beans>


I get an "org.hibernate.HibernateException: collection is not associated with any session" during the Hibernate.initialize() call. Logs show "EntityManagerFactoryUtils - Closing JPA EntityManager" message before the Hibernate.initialize call, indicating that the EM is opened and closed during the em.find, as opposed to being left open during the whole @Transactional method call, as the annotation would indicate.

Any ideas?

Alex

Marten Deinum
Sep 15th, 2009, 12:18 AM
Apart from the fact that I would shudder on such a class it should work. However you should use classproxying for the transactions, which adds a dependency on cglib.

AlexSavitsky
Sep 15th, 2009, 08:48 PM
Cleared it up - apparently, it was the presence of both the ContextLoaderListener and DispatcherServlet configs - they both loaded at startup, and somehow conflicted with each other. When I removed the listener, everything started to work.

Why shudder, though? Personally, after the advent of JPA, I tend to find DAOs largely irrelevant (except for some really complex cases), and for a simple CRUD app (which mine is), service layer seem to be overkill as well. Must have been the Seam influence...