Reputation: 486
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-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
@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;
}
}
@Repository
public class JpaDao {
@PersistenceContext
private EntityManager em;
@Transactional
public void addProfessor(Professor professor) {
em.persist(professor);
em.flush();
}
}
<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
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.
Putting another <tx:annotation-driven />
into spring servlet config fixes the problem, but why?
Upvotes: 8
Views: 10685
Reputation: 7455
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-package
configuration 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 :-(
Upvotes: 5
Reputation: 8322
The @Transactional
annotation may be placed before an interface definition, a method on an interface, a class definition, or a public method on a class. However, please note that the mere presence of the @Transactional
annotation is not enough to actually turn on the transactional behavior - the @Transactional
annotation is simply metadata that can be consumed by something that is @Transactional
-aware and that can use the metadata to configure the appropriate beans with transactional behavior. In the case of the above example, it is the presence of the <tx:annotation-driven/>
element that switches on the transactional behavior.
from spring doc http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html
Upvotes: 0
Reputation: 403441
Just a guess, but you don't need to register your own PersistenceAnnotationBeanPostProcessor
, since <context:component-scan>
registers one automatically. It's possible that the two are interfering with one another.
Like I said, though, just a hunch.
Upvotes: 0