Avatar
0
Dũng Trần Văn Beginner
JPA deleteAll trong mối quan hệ OneToMany

Em chào mọi người ạ. Em đang gặp một trường hợp muốn được giải thích rõ hơn với Hibernate/JPA và mong mọi người giải thích giúp em cơ chế hoạt động.

Em có 2 entity là A và B:
Một A có nhiều B.
Một B chỉ thuộc về một A.
Trong entity A em khai báo như sau:
@OneToMany(
    mappedBy = "parent",
    fetch = FetchType.LAZY,
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<B> childrens = new ArrayList<>();
Trong service em có đoạn code đại khái như sau:
@Transactional
public void deleteChildren(Long aId, List<Long> deleteIds) {

    A parent = parentRepository.findById(aId)
            .orElseThrow();

    List<B> existingChildren = childRepository.findByParentId(aId);

    // kiểm tra deleteIds có thuộc về parent hay không

    List<B> childrenToDelete = existingChildren.stream()
            .filter(child -> deleteIds.contains(child.getId()))
            .toList();

    childRepository.deleteAll(childrenToDelete);
}
Trong đoạn service trên em ko hề gọi fetch hay getChidren() list B của A, tuy nhiên khi em chạy test thành công nhưng checkDB thì thấy dữ liệu chưa được xóa đi. Em đã có tham khảo qua chat những vẫn chưa nhận đưcọ câu trả lời chính xác.
Ẻm cảm ơn mọi người ạ.
  • Answer
Remain: 5
1 Answer
Avatar
tvd12 Enlightened
tvd12 Enlightened
Nguyên nhân có thể là do hibernate vẫn đang quản lý parent-children trong context của nó ở đâu đó, khiến khi lưu parent lại lưu lại children.
Em thử gọi thêm parentRepository.save(parent); xem sao.
Ngoài ra:
Đầu tiên, đừng bao giờ dùng mấy cái annotation @OneToMany, trong thực tế dữ liệu rất nhiều nó có thể gây sập server.
Thứ 2. Đừng bao giờ gọi kiểu này List<B> existingChildren = childRepository.findByParentId(aId); nó lấy rất tất cả bản ghi liên quan cũng có thể gây sập server. hãy dùng jpql kiểu này:
public interface ChildRepository extends JpaRepository<B, Long> {

    @Modifying(clearAutomatically = true, flushAutomatically = true)
    @Query("""
        DELETE FROM B child
        WHERE child.parent.id = :parentId
          AND child.id IN :deleteIds
        """)
    int deleteByParentIdAndIds(
        @Param("parentId") Long parentId,
        @Param("deleteIds") Collection<Long> deleteIds
    );
}

@Transactional
public void deleteChildren(Long parentId, Collection<Long> deleteIds) {
    if (deleteIds == null || deleteIds.isEmpty()) {
        return;
    }

    if (!parentRepository.existsById(parentId)) {
        throw new EntityNotFoundException(
            "Parent not found: " + parentId
        );
    }

    int deletedCount = childRepository.deleteByParentIdAndIds(
        parentId,
        deleteIds
    );

    if (deletedCount != deleteIds.size()) {
        throw new IllegalArgumentException(
            "One or more child IDs do not belong to parent: " + parentId
        );
    }
}
  • 0
  • Reply
0%