Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在ControllerTest中使用@WebAppConfiguration而不是@SpringBootTest时,Spring数据JPA中忽略了惰性初始化_Java_Spring_Spring Boot_Spring Data Jpa_Spring Transactions - Fatal编程技术网

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);  
  }
}