如何测试LazyInitializationException?

时间:2013-06-15 11:06:07

标签: java hibernate spring-transactions spring-test

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

  • 在一个线程中,使用db
  • 中的数据填充缓存
  • 在另一个线程中,从缓存中获取数据,并开始迭代它的属性。

这引起了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

1 个答案:

答案 0 :(得分:0)

您需要确保每个操作的事务都是相互独立的。使用@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

此致

萨姆

P.S。请注意,此技术自然会将更改提交到您的数据库。因此,如果您需要清理修改后的状态,请考虑使用@AfterTransaction方法手动执行此操作。