1

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

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

这抛出了一个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?

4

1 回答 1

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.

问候,

山姆

ps 请注意,这种技术自然会将更改提交给您的数据库。因此,如果您需要清理修改后的状态,请考虑在@AfterTransaction方法中手动执行此操作。

于 2013-06-15T15:30:52.923 回答