我有一些代码(正在生产中):


在一个线程中,使用数据库中的数据来填充缓存
在另一个线程中,从缓存中获取数据,然后开始迭代其属性。


这引发了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方法手动进行。

09-09 23:35
查看更多