【问题标题】:LazyInitializationException in unit tests under Spring-Data/Spring-BootSpring-Data/Spring-Boot 下单元测试中的 LazyInitializationException
【发布时间】:2021-04-23 19:50:18
【问题描述】:

我的单元测试看到org.hibernate.LazyInitializationException: could not initialize proxy [org.openapitools.entity.MenuItem#5] - no Session。我不确定他们为什么希望在单元测试中进行会话。我正在尝试为实现 RESTful API 的 Controller 类的单元测试写入内存中的 h2 数据库。我没有使用任何模拟对象进行测试,因为我想测试实际的数据库事务。这在我使用 Spring-Boot 版本 1.x 时效果很好,但在我移到版本 2 时就坏了。(我不确定这是否是导致测试中断的原因,因为我做了很多其他更改。我的观点是我的代码已经通过了这些测试。)

我的存储库扩展了 JPARepository,所以我使用标准的 Hibernate 接口。

在 StackOverflow 上有很多关于这个问题的答案,但很少有人描述我可以与 Spring-Data 一起使用的解决方案。

附录:下面看一下单元测试:

@Test
public void testDeleteOption() throws ResponseException {
  MenuItemDto menuItemDto = createPizzaMenuItem();
  ResponseEntity<CreatedResponse> responseEntity 
      = adminApiController.addMenuItem(menuItemDto);
  final CreatedResponse body = responseEntity.getBody();
  assertNotNull(body);

  Integer id = body.getId();
  MenuItem item = menuItemApiController.getMenuItemTestOnly(id);
  // Hibernate.initialize(item); // attempted fix blows up
  List<String> nameList = new LinkedList<>();
  for (MenuItemOption option : item.getAllowedOptions()) { // blows up here
    nameList.add(option.getName());
  }
  assertThat(nameList, hasItems("pepperoni", "olives", "onions"));
  // ... (more code)
}

我的测试 application.properties 有这些设置

spring.datasource.url=jdbc:h2:mem:pizzaChallenge;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.username=pizza
spring.datasource.password=pizza
spring.jpa.show-sql=true

【问题讨论】:

  • 你能发布一些代码吗?

标签: java spring-boot hibernate spring-data


【解决方案1】:

这不是标准的Hibernate,而是spring数据。您必须了解 Hibernate 使用延迟加载来避免从数据库中加载整个对象图。如果您关闭会话或与数据库的连接,例如通过结束事务,Hibernate 不能再延迟加载,显然,您的代码会尝试访问需要延迟加载的状态。

您可以在您的存储库上使用@EntityGraph 来指定应获取关联或避免访问未在事务之外初始化的状态。也许你只需要在调用仓库和访问状态的方法上加上@Transactional来扩大事务范围,这样延迟加载就可以工作了。

【讨论】:

  • 我应该补充一点,它在生产中运行良好。我只需要修复单元测试。所以我犹豫是否要更改生产代码来解决这个问题。
  • 另外,没有会话。
  • 也许在生产中实体MenuItem#5通过其他方式放入一级缓存中,这在您的测试中不会发生,但我不会依赖这个。
【解决方案2】:

我找到了解决这个问题的方法。我不确定这是否是最好的方法,所以如果有人有更好的想法,我将不胜感激。

这就是我所做的。首先,在从延迟加载的实体中读取值之前,我调用Hibernate.initialize(item);

这会引发相同的异常。但现在我可以在 application.properties 的测试版本中添加一个属性,上面写着

spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

现在初始化方法可以工作了。

附:我还没有找到像这样的 Spring 属性的良好参考。如果有人知道我在哪里可以看到可用的属性,我很想听听。 Spring 的人并没有很好地记录这些属性。即使他们提到了一个特定的属性,他们也没有提供一个可以更彻底地解释它的链接。

【讨论】:

  • 你是参考。这解决了我的问题,谢谢!!!
猜你喜欢
  • 2019-01-23
  • 2016-05-28
  • 1970-01-01
  • 2017-02-08
  • 2019-05-19
  • 2018-03-01
  • 2015-12-20
  • 1970-01-01
相关资源
最近更新 更多