我有一些代码(正在生产中):
在一个线程中,使用数据库中的数据来填充缓存
在另一个线程中,从缓存中获取数据,然后开始迭代其属性。
这引发了LazyInitializationException
。
虽然我知道如何解决问题,但我想对此进行测试。但是我无法弄清楚如何在测试的正确部分中重新创建异常。
我必须用一些测试数据来填充数据库,因此测试用@Transactional
注释。否则会导致设置失败并...您猜到了... LazyInitializationException
。
这是我目前的测试:
@Transactional
public class UpdateCachedMarketPricingActionTest extends AbstractIntegrationTest {
@Autowired
private UpdateCachedMarketPricingAction action;
@Autowired
private PopulateMarketCachesTask populateMarketCachesTask;
@Test @SneakyThrows
public void updatesCachedValues()
{
// Populate the cache from a different thread, as this is how it happens in real life
Thread updater = new Thread(new Runnable() {
@Override
public void run() {
populateMarketCachesTask.populateCaches();
}
});
updater.start();
updater.join();
updateMessage = {...} //ommitted
action.processInstrumentUpdate(updateMessage);
}
因此,我在一个单独的线程中启动我的缓存,以尝试使其不在当前
@Transaction
范围之外。另外,我还在缓存入门中调用entityManager.detatch(entity)
,以确保缓存中存在的实体不会延迟加载其集合。但是,测试通过了……没有异常抛出。
如何强制实体进入下一次尝试迭代其集合的状态,该实体将抛出
LazyInitializationException
? 最佳答案
您需要确保每个操作的事务都相互独立地提交。用@Tranactional
注释测试方法或测试类将使当前测试事务处于打开状态,然后在执行整个测试后将其回滚。
因此,一种选择是执行以下操作:
@Autowired
private PlatformTransactionManager transactionManager;
@Test
public void example() {
new TransactionTemplate(transactionManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
// add your code here...
}
});
}
您可以在自己的回调中调用第一个操作,然后在另一个回调中调用第二个操作。然后,当您在回调之后访问Hibernate或JPA实体时,这些实体将不再附加到当前的工作单元(例如Hibernate
Session
)。因此,此时访问惰性集合或字段将导致LazyInitializationException
。问候,
山姆
ps。请注意,这种技术自然会将更改提交给数据库。因此,如果需要清除该修改状态,请考虑使用
@AfterTransaction
方法手动进行。