【问题标题】:Test @OneToMany in Spring Boot在 Spring Boot 中测试 @OneToMany
【发布时间】:2021-01-03 09:06:39
【问题描述】:

我有以下实体:

@Getter
@Setter
@NoArgsConstructor
@MappedSuperclass
public class BaseEntity implements Serializable{
    
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;


    public BaseEntity(Long id) {
        this.id = id;
    }

    public boolean isNew(){
        return this.id == null;
    }
}

@Setter
@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
@Table(name = "project")
public class Project extends BaseEntity{

    private static final long serialVersionUID = 1L;

    @Column
    @NotBlank(message = "Name property cannot be null or empty")
    private String name;

    @Column
    private String description;

    @Column
    private LocalDateTime created;

    @Column
    private Long parentId;
    
    @OneToMany(mappedBy = "parentId", cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)
    private Set<Project> subprojects = new HashSet<>();

    @Builder
    public Project(Long id, String name, String description, LocalDateTime created, Long parentId) {
        super(id);
        this.name = name;
        this.description = description;
        this.created = created;
        this.parentId = parentId;
}

和通常的存储库:

@Repository
public interface ProjectRepository extends JpaRepository<Project, Long>{
}

这里是集成测试类:

@DataJpaTest
class ProjectRepositoryIT {
    
    @Autowired
    TestEntityManager testEntityManager;
    
    @Autowired
    ProjectRepository projectRepository;
    
    @Test
    @Transactional
    void testSaveSubproject() {
        Project parent = Project.builder().name("parent").build();
        parent = testEntityManager.persistAndFlush(parent);
        
        Project child = Project.builder().name("child").parentId(parent.getId()).build();
        child = testEntityManager.persistAndFlush(child);
        
        var optionalParent = projectRepository.findById(parent.getId());
        
        if(optionalParent.isPresent()) {
            var foundParent = optionalParent.get();
            
            assertEquals(parent.getId(), foundParent.getId());
            assertEquals(1, foundParent.getSubprojects().size());
            
            Optional<Project> matchigProject = foundParent.getSubprojects()
                .stream()
                .filter(p -> p.getId().equals(foundParent.getId()))
                .findFirst();
            
            assertTrue(matchigProject.isPresent());
    
            assertEquals(child.getId(), matchigProject.get().getId());
        }else {
            fail("cannot find parent project");
        }
    }
}

问题

我正在尝试测试是否可以通过设置parentId 属性正确标记子项目。我希望在设置parentId 属性时,使用此id 的项目应该在subprojects 列表中包含有关子项目的信息。不幸的是,断言assertEquals(1, foundParent.getSubprojects().size()); 失败,因为列表大小为0。我想请教一下我做错了什么?我需要宣布,我是 Spring 的新手。

【问题讨论】:

    标签: java spring spring-boot spring-data-jpa spring-test


    【解决方案1】:

    这是一个很常见的错误。 要使其正常工作,您可以执行以下热修复:

        Project parent = Project.builder().name("parent").build();
        Project child = Project.builder().name("child").build();
        parent.getSubprojects().add(child);
    
        parent = testEntityManager.persistAndFlush(parent);
    
        child.setParentId(parent.getId());
        child = testEntityManager.persistAndFlush(child);
    

    Spring 不会自动将子实体中的 parentId 与父实体中的子项目集绑定,没有魔法。您必须将子实体放在父实体的子项目集中保存,然后自己设置子实体的 parentId 并仔细管理。

    【讨论】:

      【解决方案2】:

      感谢@Alex Crazy 的回答。我的目标是让它尽可能简单。我希望每个Project 都有对父级的引用,当父级被删除时,所有子级也会被删除。

      我通过以下方式修复:

      Project 类只有 parentId 字段,没有任何 @OneToMany@ManyToOne 链接:

      ...
      public class Project extends BaseEntity{
      ...
          @Column
          private Long parentId;
      ...
      }
      

      然后ProjectRepository通过以下搜索扩展:

      @Repository
      public interface ProjectRepository extends JpaRepository<Project, Long>{
          
          List<Project> findAllByParentId(Long id);
          
          void deleteAllByParentId(Long id);
      }
      

      和测试:

          @Test
          void testFindSubprojects() {
              // Prepare database
              Project parent = Project.builder().name("parent").build();
              parent = testEntityManager.persistAndFlush(parent);
              
              Project child = Project.builder().name("child").parentId(parent.getId()).build();
              child = testEntityManager.persistAndFlush(child);
              
              // Call repository
              Project foundParent = projectRepository.findById(parent.getId()).orElseThrow(() -> fail());
              assertNotNull(foundParent.getId());
              
              List<Project> subprojects = projectRepository.findAllByParentId(foundParent.getId());
              assertEquals(1, subprojects.size());
              assertEquals(child.getId(), subprojects.get(0).getId());
              assertEquals(child.getParentId(), foundParent.getId());
          }
          
          @Test
          void testDeleteAllByParentId() {
              // Prepare database
              Project parent = Project.builder().name("parent").build();
              parent = testEntityManager.persistAndFlush(parent);
              
              testEntityManager.persistAndFlush(Project.builder().name("child1").parentId(parent.getId()).build());
              testEntityManager.persistAndFlush(Project.builder().name("child2").parentId(parent.getId()).build());
              testEntityManager.persistAndFlush(Project.builder().name("child3").parentId(parent.getId()).build());
              
              var subprojects = projectRepository.findAllByParentId(parent.getId());
              assertEquals(3, subprojects.size());
              
              projectRepository.deleteAllByParentId(parent.getParentId());
              
              subprojects = projectRepository.findAllByParentId(parent.getParentId());
              assertEquals(0, subprojects.size());
          }
      

      删除 ass 嵌套子项目将由服务层管理。我希望这是一个很好的解决方案。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-04-28
        • 1970-01-01
        • 2022-01-23
        • 2020-08-01
        • 1970-01-01
        • 2014-11-13
        • 2019-03-13
        • 2016-10-29
        相关资源
        最近更新 更多