繁体   English   中英

在 Spring 引导中测试 @OneToMany

[英]Test @OneToMany in Spring Boot

我有以下实体:

@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>{
}

这是集成测试 class:

@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 的新手。

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

    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 并仔细管理。

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

我通过以下方式修复:

Class 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 嵌套子项目将由服务层管理。 我希望这是一个很好的解决方案。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM