Declarative transactions (@Transactional) doesn't work with @Repository in Spring

10,361

Probably because the component-scan in your spring-servlet.xml is also including your DAO classes in its scanning and therefore creating instances for them in its application context (not the "database" one)... so that when your web accesses these DAOs from web controllers, it is accessing non-transactional versions of them (unless you add that tx:annotation-driven tag).

Therefore, adding that tag is in fact a bad solution because it still creates your DAO instances in the wrong application context: better create a more specific base-packageconfiguration for your web layer component creation.

I had this same problem because I thought a <context:include-filter> in my spring-servlet.xml was taking care of only scanning @Controller classes... but no :-(

Share:
10,361
SpyBot
Author by

SpyBot

Software Engineer

Updated on June 04, 2022

Comments

  • SpyBot
    SpyBot almost 2 years

    I'm trying to make simple application using Spring, JPA and embedded H2 database. Recently I've come across this strange issue with declarative transactions. They just doesn't commit if I autowire my DAO with @Repository annotation. More specifically I get exception on flush:

    javax.persistence.TransactionRequiredException: 
    Exception Description: No transaction is currently active
    

    Here is my setup:

    persistence.xml

    <persistence-unit name="schedulePU" transaction-type="RESOURCE_LOCAL">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <properties>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:~/scheduleDB" />
            <property name="javax.persistence.jdbc.user" value="sa" />
            <property name="javax.persistence.jdbc.password" value="" />
            <property name="eclipselink.target-database" value="org.eclipse.persistence.platform.database.H2Platform" />
            <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
            <property name="eclipselink.logging.level" value="FINE"/>
        </properties>
    </persistence-unit>
    

    Entity

    @Entity
    @Table(name = "Professors")
    public class Professor {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private int id;
        private String name;
    
        public Professor() { }
    
        public Professor(String name) {
            this.name = name;
        }
    }
    

    DAO

    @Repository
    public class JpaDao {
    
        @PersistenceContext
        private EntityManager em;
    
        @Transactional
        public void addProfessor(Professor professor) {
            em.persist(professor);
            em.flush();
        }
    }
    

    database.xml (included from root spring context)

    <beans>
        <context:component-scan base-package="com.spybot.schedule.dao" />
    
        <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
    
        <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
            <property name="persistenceUnitName" value="schedulePU" />
        </bean>
    
        <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
            <property name="entityManagerFactory" ref="entityManagerFactory" />
        </bean>
    
        <tx:annotation-driven transaction-manager="transactionManager" />
    </beans>
    

    Controller

    @Controller
    public class HomeController {
    
        @Inject
        JpaDao dao;
    
        @RequestMapping("/add")
        public @ResponseBody String add(String name) {
            Professor p = new Professor(name);
            dao.addProfessor(p);
            return ":)";
        }
    }
    

    And now the interesting part. If I remove @Repository annotation from DAO and specify it explicitly in database.xml, everything works fine.

    Update

    Putting another <tx:annotation-driven /> into spring servlet config fixes the problem, but why?

  • skaffman
    skaffman over 12 years
    Do you really want to quote the Spring docs from version 2.0.8?
  • SpyBot
    SpyBot over 12 years
    Did you even read the question? I've had <tx:anotation-driven /> in my application context. I'm asking why putting it additionaly into servlet context solves the problem.