Java 在ControllerTest中使用@WebAppConfiguration而不是@SpringBootTest时,Spring数据JPA中忽略了惰性初始化
我使用以下模型:Java 在ControllerTest中使用@WebAppConfiguration而不是@SpringBootTest时,Spring数据JPA中忽略了惰性初始化,java,spring,spring-boot,spring-data-jpa,spring-transactions,Java,Spring,Spring Boot,Spring Data Jpa,Spring Transactions,我使用以下模型: @Entity public class User { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) private Long id; private String username; @ManyToMany(fetch = FetchType.LAZY) @JoinTable(name = "USER_ROLE", joinColumns = {@JoinCo
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String username;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "USER_ROLE", joinColumns = {@JoinColumn(name = "USER_ID", referencedColumnName = "ID")}, inverseJoinColumns = {@JoinColumn(name = "ROLE_ID", referencedColumnName = "ID")})
private List<Role> roles;
(...)
}
@Entity
public class Role {
private String name;
@ManyToMany(mappedBy = "roles", fetch = FetchType.LAZY)
private List<User> users;
(...)
}
应用程序是普通的,不使用任何与事务相关的注释(但main是用@SpringBootApplication注释的)
我只在application.properties:spring.jpa.open in view=false中使用一个属性
测试过程如下:
@RunWith(SpringRunner.class)
@WebAppConfiguration @ContextConfiguration(classes = Application.class)
@AutoConfigureMockMvc
public class ControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void test() throws Exception {
this.mockMvc.perform(get("/test").param("u", "user1")).andExpect(status().isOk());
}
}
然后我的问题是,当调试看起来像是所有的实体图都被急切地抓取,并且测试成功返回,因为控制器并没有失败
我知道JPA存储库方法使用事务,但据我所知,只要我们不将它们包装到更高级别的事务中,它应该在方法结束后完成。那么,在尝试访问roler或user->role->users时,我怎么可能没有收到LazyInitialization异常呢??这是预期的行为吗
这是Eclipse debugger在显示用户角色中的用户时显示的内容,值得注意:
Hibernate日志显示了它在查找用户时如何急切地获取关系:
12:47:24.890 [main] DEBUG org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer - Loading collection: ..domain.User.roles
12:47:24.937 [main] DEBUG org.hibernate.loader.plan.exec.process.internal.CollectionReferenceInitializerImpl - Found row of collection: [User.roles#1002]
12:47:24.953 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Resolving associations for [...domain.Role#5]
12:47:24.953 [main] DEBUG org.hibernate.engine.internal.TwoPhaseLoad - Done materializing entity [...domain.Role#5]
12:47:24.953 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections were found in result set for role: ...domain.User.roles
12:47:24.968 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - Collection fully initialized: [...domain.User.roles#1002]
12:47:24.968 [main] DEBUG org.hibernate.engine.loading.internal.CollectionLoadContext - 1 collections initialized for role: ....domain.User.roles
12:47:24.968 [main] DEBUG org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - HHH000387: ResultSet's statement was not registered
12:47:24.968 [main] DEBUG org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer - Done loading collection
编辑:我发现如果我通过现代@SpringBootTest
更改@WebAppConfiguration
上的@ControllerTest
行,我会得到LazyInitializationException
。为什么@WebAppConfiguration
旧方法会忽略延迟抓取?Spring Boot默认注册OpenEntityManagerViewInterceptor
以应用视图中的OpenEntityManager模式,从而允许在web视图中延迟加载。如果不希望出现这种行为,则应在应用程序.properties中将spring.jpa.open in view
设置为false
编辑:(编辑问题后)
实际上,您应该有一个LazyInitializationException。我猜在您的测试中,会话
仍然打开
如果您使用@SpringBootTest
进行测试,您将得到一个LazyInitializationException
@RunWith(SpringRunner.class)
@SpringBootTest
public class LazyLoadingExceptionTest {
@Autowired UserRepository userRepository;
@Test(expected=LazyInitializationException.class)
public void showRolesTest() {
User whimusical = userRepository.findByUsername("Whimusical");
System.err.println(whimusical.getRoles());
}
}
但是如果您使用@DataJpaTest
进行测试,您不会得到异常
@RunWith(SpringRunner.class)
@DataJpaTest
public class NoLazyLoadingExceptionTest {
@Autowired UserRepository userRepository;
@Test
public void showRolesTest() {
User whimusical = userRepository.findByUsername("Whimusical");;
System.err.println(whimusical.getRoles());
}
}
触发懒散初始化异常的另一种方法
覆盖用户的toString()
@Override
public String toString() {
return username + " with roles: " + roles;
}
并使用此组件启动应用程序
@Component
public class DataSetup implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
List<Role> roles = new ArrayList<>();
Role role = new Role();
role.setName("user");
roles.add(roleRepository.save(role));
User whimusical = new User();
whimusical.setUsername("Whimusical");
whimusical = userRepository.save(whimusical);
whimusical.setRoles(roles);
whimusical = userRepository.save(whimusical);
userRepository.findAll().forEach(System.err::println);
}
}
@组件
公共类数据设置实现CommandLineRunner{
@凌驾
公共无效运行(字符串…参数)引发异常{
列表角色=新的ArrayList();
角色=新角色();
role.setName(“用户”);
roles.add(roleRepository.save(role));
用户=新用户();
设置用户名(“WhiMusic”);
WhiMusic=userRepository.save(WhiMusic);
设置角色(角色);
WhiMusic=userRepository.save(WhiMusic);
userRepository.findAll().forEach(System.err::println);
}
}
谢谢!但似乎并不是这样,仍然急切地想拿着房产过活。我不是在使用视图,至少不是明确地使用视图,而是通过测试执行控制器,并在调用存储库时调试用户。我找到一些时间将代码对分,直到我将示例重构为最小表达式和大多数引导约定。最后一块总结了一个非常重要的发现,它大大缩小了问题的范围
@RunWith(SpringRunner.class)
@DataJpaTest
public class NoLazyLoadingExceptionTest {
@Autowired UserRepository userRepository;
@Test
public void showRolesTest() {
User whimusical = userRepository.findByUsername("Whimusical");;
System.err.println(whimusical.getRoles());
}
}
@Override
public String toString() {
return username + " with roles: " + roles;
}
@Component
public class DataSetup implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
List<Role> roles = new ArrayList<>();
Role role = new Role();
role.setName("user");
roles.add(roleRepository.save(role));
User whimusical = new User();
whimusical.setUsername("Whimusical");
whimusical = userRepository.save(whimusical);
whimusical.setRoles(roles);
whimusical = userRepository.save(whimusical);
userRepository.findAll().forEach(System.err::println);
}
}