切换到JPA,现在我在任何插入时都会得到TransactionRequiredException



我有一个Spring4 web应用程序。最初,我使用Hibernate SessionFactory,并使用SpringHibernate API进行开发。一切都很好。也许我最近决定改用JPA,而Hibernate仍然是我的提供者,这很愚蠢。我重新配置了Spring设置,并重写了大部分代码。最初测试了一个,这样我所有的数据库读取都能工作,他们做到了。然后我尝试了数据库写入,但他们都失败了:

javax.persistence.TransactionRequiredException: no transaction is in progress
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:970)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:342)
    at com.sun.proxy.$Proxy47.flush(Unknown Source)
    at com.taubler.oversite.dao.impl.EntityDaoImpl.insert(EntityDaoImpl.java:65)
    ...

请记住,当使用HibernateTemplateSessionFactoryHibernateTransactionManager等时,我的代码运行良好。我的业务逻辑类以及我的DAO都像以前一样用@Transactional进行了注释。

看起来Hibernate正试图创建一个事务,正如我在堆栈跟踪之前的日志中看到的那样:

2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - initial autocommit status: true
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - disabling autocommit
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.internal.SessionImpl - Opened session at timestamp: 14115372286
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.event.internal.AbstractSaveEventListener - Transient instance of: com.taubler.oversite.entities.EmailAddress
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.event.internal.DefaultPersistEventListener - Saving transient instance
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.event.internal.AbstractSaveEventListener - Saving [com.taubler.oversite.entities.EmailAddress#<null>]
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.engine.spi.IdentifierValue - ID unsaved-value: null
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Delaying identity-insert due to no transaction in progress
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.internal.SessionImpl - Opened session at timestamp: 14115372287
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - rolling back
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - rolled JDBC Connection
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - re-enabling autocommit
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.engine.transaction.internal.TransactionCoordinatorImpl - after transaction completion
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - after transaction completion
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.internal.SessionImpl - Closing session
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Closing logical connection
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.engine.jdbc.internal.JdbcResourceRegistryImpl - Closing JDBC container [org.hibernate.engine.jdbc.internal.JdbcResourceRegistryImpl@2c4e3947]
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Releasing JDBC connection
2014-09-24 05:40:28 [http-bio-8080-exec-3] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Released JDBC connection
2014-09-24 05:40:28 [http-bio-8080-exec-3] TRACE org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Logical connection closed

这是一些相关的代码。首先,我的spring-config XML:中的片段

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
      <property name="dataSource" ref="dataSource" />
      <property name="packagesToScan" value="com.taubler.oversite.entities" />
      <property name="jpaVendorAdapter">
         <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
      </property>
      <property name="jpaProperties">
         <props>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.jdbc.batch_size">20</prop>
            <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>               
         </props>
      </property>
</bean>
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="${db.driver}" />
    <property name="url" value="${db.url}" />
    <property name="username" value="${db.username}" />
    <property name="password" value="${db.password}" />
</bean>

一个示例业务逻辑(管理器)类。这个类是SpringMVC控制器中的@Autowired,因此控制器正在调用代理:

...
@Autowired
private EmailAddressDao emailAddressDao;
...
@Override
@Transactional
public EmailAddress addEmailAddress(User user, String email) {  
    EmailAddress emailAddress = new EmailAddress(user, email);
    emailAddress.setMain(false);
    emailAddress.setValidated(false);
    emailAddressDao.insert(emailAddress);
    this.initiateEmailValidation(emailAddress);
    return emailAddress;
}

该经理调用的DAO:

...
@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
protected final EntityManager getEntityManager() {
    return entityManagerFactory.createEntityManager();
}
public boolean insert(Entity o) {
    o.setCreated(new Date());
    this.getEntityManager().persist(o);
    this.getEntityManager().flush();
    return true;
}

我试过不同的变体。最初,DAO和Manager方法都用@Transactional(propagation=REQUIRED);进行了注释,这就是它在纯Hibernate中的工作方式。我尝试删除传播设置,只注释Manager方法,只注释DAO方法。。。什么都不管用。

有什么想法吗?CCD_ 8和CCD_。

@PersistenceUnit
private EntityManagerFactory entityManagerFactory;
protected final EntityManager getEntityManager() {
    return entityManagerFactory.createEntityManager();
}

问题是你自己在创建一个实体经理,不要。只需注入EntityManager而不是EntityManagerFactory,并用@PersistenceContext而不是@PersistenceUnit进行注释。Spring将负责将其绑定到当前事务。

@PersistenceContext
private EntityManager entityManager;
protected final EntityManager getEntityManager() {
    return entityManger;
}

如果您真的想继续注入EntityManagerFactory,请使用EntityManagerFactoryUtilsgetTransactionalEntityManager方法来获得Spring管理的EntityManager实例。

protected final EntityManager getEntityManager() {
     return EntityManagerFactoryUtils.getTransactionalEntityManager(entityManagerFactory);
}

此外,它与"普通"hibernate一起工作并不意味着你的设置必须是正确的。您提到您使用了HibernateTemplate,它基本上可以在没有适当事务设置的情况下工作,因为它只会为手头的操作启动新的事务。因此,很可能应用程序在实际不工作的地方似乎工作正常。也许您有多个事务,而您期望有一个事务(来自服务层)。

另一个注意事项是,你的代码可能是危险的

public boolean insert(Entity o) {
    o.setCreated(new Date());
    this.getEntityManager().persist(o);
    this.getEntityManager().flush();
    return true;
}

在您的情况下,这可能会导致创建两个不同的EntityManager,因此您可能在持久化时刷新了另一个。除此之外,您不应该调用flush,因为这将在事务结束时完成。

您需要使用@PersistenceContext注释直接注入EntityManager,而不是PersistenceUnit

最新更新