我正在使用带弹簧的eclipselink。我有一些JPA实体和DAO实现为spring bean。对于事务管理,我使用的是JtaTransactionManager。这是我的春季配置:
<beans>
<context:annotation-config />
<context:component-scan base-package="daopackage" />
<tx:annotation-driven proxy-target-class="true" />
<tx:jta-transaction-manager />
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.jboss.JBossLoadTimeWeaver"/>
</property>
</bean>
</beans>
另外,我显然有persistence.xml,但是我会跳过它,没有什么不寻常的,而且持久性本身也很好。
问题在于:在我看来,即使在使用JtaTransactionManager的情况下,Spring仅将所有事务资源与其内部事务上下文同步,并且不会将其绑定到JTA事务上下文。 DAO代码:
class Dao {
@PersistenceContext
protected EntityManager manager;
@Transactional(propagation = MANDATORY)
public JpaEntity getById(BigInteger id) {
return manager.find(JpaEntity.class, id);
}
@Transactional(propagation = MANDATORY)
public void remove(JpaEntity entity) {
manager.remove(entity)
}
}
测试代码:
UserTransaction transaction = getTransaction(); // lookup by jndi name
transaction.begin();
Dao dao = getDao(); // obtain from ApplicationContext
JpaEntity entity = dao.getById(id);
dao.remove(entity);
transaction.commit();
注入bean的EntityManager是共享实体管理器代理,它应该从绑定到当前事务的线程本地持有者获得实体管理器实现。至于当前事务是由JTA创建的,getById()和remove()的调用应该使用相同的实体管理器实现,其缓存一直存在直到commit()调用。但是,此代码在remove()调用期间产生错误 - 实体管理器认为实体已分离。
调试显示共享实体管理器代理对getById()和remove()调用使用不同的实体管理器。我已经启动了Spring代码调试并发现Spring事务拦截器在调用截获的方法后实际执行内部提交操作,包括解除所有线程本地资源(实体管理器所在的位置)和调用事务同步侦听器(在这种情况下 - 关闭实体管理器) )。注意,传播属性是MANDATORY,并且在拦截器调用之前,事务是在外部启动的。对我来说似乎很自然,在JTA事务提交之前,不应该处理线程本地资源并且不会调用侦听器。
有人可以帮我找到使其正常工作的方法吗?