【发布时间】:2012-09-06 07:21:06
【问题描述】:
在 spring/hibernate 应用程序中,我每晚都会运行一些 cronjobs。其中一个应该像这样在多个事务中完成它的工作:
@Scheduled(cron = "...")
public void cron ( )
{
batchJob();
}
@Transactional(propagation = Propagation.NEVER)
public void batchJob ( )
{
List<Customer> customers = getCustomers();
for (Customer customer : customers
{
doSomething(customer);
}
}
@Transactional(readOnly = true)
protected List<Customer> getCustomers ( )
{
return customerRepository.getCustomers();
}
@Transactional
protected void doSomething (Customer customer )
{
// LazyInitializationException
customer.getAddress();
// ...
}
我的 spring 配置的一部分:
<!-- Transaction -->
<tx:annotation-driven mode="aspectj" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
这里的关键点是我不想有一个长时间运行的事务。因此,首先我获取所有客户并为每个客户调用一个事务方法。 (希望贴出的代码足以理解问题)
当然,我得到一个 LazyInitializationException,因为当围绕“getCustomer”的事务被提交时,Spring 正在关闭会话。
我能想到的可能解决方案:
我可以使用 OpenSessionInViewInterceptor,但这是一个 Web 组件
我可以使用 session.merge(customer) 重新附加分离的对象
这是 Propagation.Nestedtransaction 的情况吗?嵌套事务的语义是什么?大多数数据库没有嵌套事务(postgresql有但我没用过,叫两阶段提交
我可以重写方法来消费 customerId 并在第二个事务中再次加载客户
现在我有两个关于我的问题的问题:
如何编写测试来重现上述错误?
我怎样才能轻松地围绕这个问题跨越一个开放的会话,或者在多个事务中完成大量工作的最佳方法是什么?
【问题讨论】:
-
我猜你的意思是
protected void doSomething (Customer customer )而不是protected void do (Customer customer )? -
您确定
aspectj模式已启用吗?因为如果它会你没有得到LazyInitializationException。 -
@OleksandrBondarenko:为什么?
batchJob()有Propagation.NEVER,因此从它调用的方法将有自己的会话。 -
@axtavt:实际上我的意思是以下想法(来自 Spring 文档的quote):
... self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. -
肯定是aspectj激活的。这不是问题。我只是用 Propagation.NEVER 标记了一种方法,以明确没有类注释或其他东西。我配置弹簧没有问题。这更像是一个架构问题。我想知道其他人是如何做这样的事情的。
标签: spring hibernate transactions