在Spring的@Transactional方法中没有任何事务开始

4

在使用Spring(3.0.5)、Hibernate(3.6.0)和Wicket(1.4.14)开发应用程序时,我遇到了一个奇怪的问题。问题是:我无法保存或修改任何对象到数据库中。所谓“无法”,是指所有对对象的更改或对EntityManager.persist(foo)的调用都被简单、默默地忽略了。选择操作可以正常工作。

一个简单的示例是,在某个Wicket页面上,我尝试将对象保存到数据库中,如下所示:

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有哪些构造函数? - Bozho
已删除wicket标签,因为它与此问题无关(您的问题不在wicket代码中)。添加了Java和JPA标签。 - Sean Patrick Floyd
3个回答

15

a) 您同时定义了Hibernate的SessionFactory和JPA的EntitymanagerFactory。您要使用哪一个呢?请使用Hibernate的Session API或者使用Hibernate作为提供程序时使用JPA的Entitymanager API,但不要同时使用两者。

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不应该是事务性的。你应该使用一个服务层来管理事务。参考这个问题(和其他许多问题)。


非常感谢您的回答。我尝试编辑applicationCOntext.xml文件 - 我只是注释了entityManagerFactory bean。现在,当我尝试在我的DAO中使用entityManager时,我得到了“org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [javax.persistence.EntityManagerFactory] is defined: expected single bean but found 0”的错误提示。 - M4ks
@M4ks 那么你应该注释掉SessionFactory,而不是EntityManagerFactory。 - Sean Patrick Floyd
我想是这样。但是如果没有entityManager,我怎么执行任何数据库操作呢? - M4ks
1
@M4ks,你误解了我的意思。要摆脱的是SessionFactory,而不是EnitiyManagerFactory。然后添加JpaTransactionManager。一切都会好起来的。 - Sean Patrick Floyd
可以了!我找到了Session API和EntityManager之间的区别。我会选择后者。非常感谢你! - M4ks
显示剩余3条评论

0

你尝试在Hibernate配置中设置hibernate.connection.autocommit=true了吗?这将解决问题。但是这种方法的效率如何,你需要自己去评估。


-2

您需要在EntityManager上调用以下方法:

flush()

以实际保存到数据库中。


3
不正确。如果正确设置了Spring TransactionManager,entityManager.flush() 将会自动调用。 - Sean Patrick Floyd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接