Search code examples
springspring-mvcspring-annotations

Spring @Transactional doesn't work with other annotations?


So my Spring education continues. Currently I'm trying to learn some of the annotations and the things they bring to Spring 3. So I've got a mini webapp that can connect to a DB and put stuff in through a form and display records and so on. Everything works fine. I decided to try and get Spring to auto-detect the service bean that I have marked as @Transactional but doing that stops the app from saving to the DB. So:

@Transactional
public class ReservationServiceImpl implements ReservationService {

that works. I have a bean declaration of this class in my springcourt-data.xml files. No problems. When I do this though:

@Transacational
@Service("reservationService")
public class ReservationServiceImpl implements ReservationService {

it no longer works. And I do have

<context:component-scan base-package="com.springcourt" />

in the springcourt-servlet.xml file. So can anyone tell me what I'm screwing up? All I do is add another annotation to this class and remove the bean definition from the xml file and it no longer saves data to the DB. I can still query records and stuff from the DB though so obviously it's using the autodetected service bean.

Here are the config files:

springcourt-servlet.xml
<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"
xsi:schemaLocation="
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

<context:component-scan base-package="com.springcourt" />

<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping" />

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    <property name="webBindingInitializer">
        <bean class="com.springcourt.web.ReservationBindingInitializer" />
    </property>
</bean>

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
</beans>

And:

springcourt-data.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="
http://www.springframework.org/schema/beans 
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

<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="true" />
        </bean>
    </property>
</bean>

<bean id="dataSource"
        class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="admin" />
    <property name="initialSize" value="5" />
</bean>

<bean id="transactionManager"
    class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<tx:annotation-driven />

<bean id="reservationService" class="com.springcourt.service.ReservationServiceImpl"/>
</beans>

Solution

  • When you use @Service and component scanning the bean is created by the context created by the dispatcher servlet (mvc). Since the transaction:annotation driven is defined in the root application context it doesn't apply to the beans in the servlets context. You can verify this by removing the @Service and moving the bean definition to the servlet context file - you should see the same result.

    Where as when you don't use component scanning - the bean is defined in the XML of the root application context.

    The fix is to change the component-scan tag in the web layer to only include web layer classes - either by using a different base package or by using an include / exclude filter. Add another component scan in the root application context for the other beans.

    Querying might be working because you might have a OpenEntityManagerInViewInterceptor / Filter configured.