Spring @Transactional 方法中没有事务开始
Posted
技术标签:
【中文标题】Spring @Transactional 方法中没有事务开始【英文标题】:No transaction starts within Spring @Transactional method 【发布时间】:2011-03-21 01:07:54 【问题描述】:我在使用 Spring (3.0.5)、Hibernate (3.6.0) 和 Wicket (1.4.14) 开发应用程序时遇到了奇怪的问题。问题是:我无法将任何对象保存或修改到数据库中。 “不能”是指对象中的所有更改或对 EntityManager.persist(foo) 的调用都被简单地、默默地忽略。选择工作。
示例案例很简单 - 在一些检票口页面上,我尝试将对象保存到数据库中,如下所示
public class ComicDetailsPage extends PublicBasePage
@Override
protected void onConfigure()
System.out.println("In onConfigure");
super.onConfigure();
comicDAO.insert("abc");
@SpringBean(name="comicDAO")
private ComicDAO comicDAO;
(....)
这里是comicDAO
@Service
public class ComicDAO
@PersistenceContext
private EntityManager em;
(...)
@Transactional
public void insert(String title)
Comic c = new Comic();
c.setTitle(title);
em.persist(c);
@Transactional
public Comic add1toTitle(int pk)
System.out.println("Beginning fetching");
Comic c = em.find(Comic.class, pk);
System.out.println("Fetched updating");
c.setTitle(c.getTitle()+"1");
System.out.println("Updated persisting");
em.persist(c);
System.out.println("Persisted returning");
return c;
我打开了日志记录,这里是日志的相关部分(Hibernate 和 Spring 都设置为 TRACE)。我在我认为重要的行中添加了 **。
In onConfigure
01:53:19.330 [qtp2119047503-15] DEBUG o.s.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'txManager'
**01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13006687993**
**01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - begin**
01:53:19.330 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection
01:53:19.335 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - current autocommit status: true
01:53:19.335 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - disabling autocommit
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction begin
01:53:19.336 [qtp2119047503-15] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13006687993
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - setting flush mode to: AUTO
01:53:19.336 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - setting cache mode to: NORMAL
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.engine.IdentifierValue - id unsaved-value: 0
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.event.def.AbstractSaveEventListener - transient instance of: pl.m4ks.comics.entity.Comic
01:53:19.337 [qtp2119047503-15] TRACE org.hibernate.event.def.DefaultPersistEventListener - saving transient instance
**01:53:19.338 [qtp2119047503-15] TRACE org.hibernate.event.def.AbstractSaveEventListener - saving [pl.m4ks.comics.entity.Comic#<null>]**
**01:53:19.341 [qtp2119047503-15] DEBUG org.hibernate.event.def.AbstractSaveEventListener - delaying identity-insert due to no transaction in progress**
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - closing session
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.jdbc.ConnectionManager - connection already null in cleanup : no action
01:53:19.341 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - commit
**01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - automatically flushing session**
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - before transaction completion
01:53:19.341 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - before transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - re-enabling autocommit
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.transaction.JDBCTransaction - committed JDBC Connection
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - after transaction completion
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - closing session
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.ConnectionManager - performing cleanup
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)]
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion
01:53:19.342 [qtp2119047503-15] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources!
01:53:19.342 [qtp2119047503-15] TRACE org.hibernate.impl.SessionImpl - after transaction completion
当然没有对象被保存到数据库中。
最后一个文件 - 我的 applicationCONtext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans (...)>
<context:component-scan base-package="pl.m4ks.comics"/>
<context:annotation-config />
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:8889/comics" />
<property name="username" value="root"/>
<property name="password" value="root" />
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="main" />
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="packagesToScan">
<value>pl.m4ks.comics</value>
</property>
</bean>
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
</beans>
我不知道可能是什么问题以及如何解决它。我不想在我的代码中管理事务——这就是 Spring 的用途。
【问题讨论】:
wicket 类中 dao 的实际类型是什么(系统出 .getClass())。 DAO 有哪些构造函数? 删除了 wicket 标签,因为它与这个问题无关(您的问题不在您的 wicket 代码中)。添加了 Java 和 JPA 标签 【参考方案1】:a) 您同时定义了 Hibernate SessionFactory
和 JPA EntitymanagerFactory
。会是哪一个?将 Hibernate 的 Session API 或 JPA 的 Entitymanager API 与 Hibernate 作为提供者一起使用,但不能同时使用两者。
b) 您已经定义了HibernateTransactionManager
,但由于您在代码中使用了EntityManager
,因此您需要一个JpaTransactionManager
:
<bean id="myTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="myEmf"/>
</bean
这是您的 applicationContext.xml 的注释版本:
<?xml version="1.0" encoding="UTF-8"?>
<beans (...)>
<context:component-scan base-package="pl.m4ks.comics"/>
<context:annotation-config />
<bean id="dataSource"
class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:8889/comics" />
<property name="username" value="root"/>
<property name="password" value="root" />
</bean>
<!-- use either this: -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="main" />
<property name="dataSource" ref="dataSource" />
</bean>
<!-- or this -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="packagesToScan">
<value>pl.m4ks.comics</value>
</property>
</bean>
<!-- (but not both) -->
<!-- this is correct for AnnotationSessionFactoryBean, but not if you use
LocalContainerEntityManagerFactoryBean -->
<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- not necessary, <context:annotation-config /> automatically includes this -->
<bean
class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
</beans>
还有一个设计说明:DAO 不应该是事务性的。您应该使用管理事务的服务层。请参阅this question(和许多其他人)以供参考。
【讨论】:
非常感谢您的回答。我试图编辑 applicationCONtext.xml - 我刚刚评论了 entityManagerFactory bean。现在我得到了org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 0
,因为我尝试在我的 DAO 中使用 entityManager。
@M4ks 那么你应该注释掉 SessionFactory,而不是 EntityManagerFactory
我想是的。但是如果没有 entityManager,我怎么能执行任何数据库操作呢?
@M4ks 你误解了我的意思。摆脱 SessionFactory,而不是 EnitiyManagerFactory。并添加 JpaTransactionManager。一切都会好起来的
有效!我发现了 Session API 和 EntityManager 之间的区别。我会留在第二个。非常感谢你!【参考方案2】:
您是否尝试过在休眠配置中设置 hibernate.connection.autocommit=true ?那将解决问题。但是这种方法的效率如何是你必须弄清楚的。
【讨论】:
【参考方案3】:您需要在 EntityManager 上调用以下方法
flush()
实际保存在数据库中
【讨论】:
这是不正确的。如果 Spring TransactionManager 设置正确,会自动调用 entityManager.flush()以上是关于Spring @Transactional 方法中没有事务开始的主要内容,如果未能解决你的问题,请参考以下文章
Spring 下默认事务机制中@Transactional 无效的原因
在 Spring 中的 @Transactional 方法期间处理异常