我有一些代码(在生产中):
- 在一个线程中,使用数据库中的数据填充缓存
- 在另一个线程中,从缓存中获取数据,并开始迭代其属性。
这引发了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
。
问候,
山姆
附:请注意,这种技术自然会将更改提交到您的数据库。因此,如果您需要清理修改后的状态,请考虑在 @AfterTransaction
方法中手动执行此操作。
关于java - 如何测试LazyInitializationException?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17122955/