簡體   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