Spring Data JPA : Xoá và các mối quan hệ

18 tháng 05, 2022 - 3310 lượt xem

Written By : Dương Minh Đức
Gmail : duc0611111@gmail.com

1.Tổng quát

Trong bài viết này , chúng ta sẽ xem xét cách xoá được thực hiện thế nào trong Spring Data Jpa
Link gốc bài viết : https://www.baeldung.com/spring-data-jpa-delete#delete-repository

2. Ví dụ với Entity

Như chúng ta đã biết từ tài liệu tham khảo Spring Data Jpa , repository interfaces cung cấp cho chúng ta một số hỗ trợ cho các thực thể :

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
   @Id
   private Long id;
   private String title;
}

Sau đó chúng ta có thể kế thừa Spring Data JPA’s CrudRepository để được cung cấp quyền truy cập vào các hoạt động CURD trong Book

@Repository
public interface BookRepository extends CrudRepository<Book,Long> {}

3. Xoá từ Repository

CrudRepository chứa 2 phương thức : deleteById và deleteAll
Hãy kiểm tra trực tiếp các phương thức từ BookRepository của chúng ta:

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class DeleteFromRepositoryUnitTest {
    @Autowired
    private BookRepository bookRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    Book book1;
    Book book2;
    List<Book> books;


    @PostConstruct
    public void initData() {
        Category category1 = new Category(1L, "anthology");
        categoryRepository.save(category1);
        Category category2 = new Category(2L, "comedy");
        categoryRepository.save(category2);
        book1 = new Book(1L, "The Hobbit", category1);
        book2 = new Book(2L, "Glory", category2);
        books = new ArrayList<>();
        books.add(book1);
        books.add(book2);
        bookRepository.saveAll(books);
    }


    @Test
    public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() {
        bookRepository.deleteById(book1.getId());
        assertThat(bookRepository.count()).isEqualTo(1);
    }
      @Test
    public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() {
        bookRepository.deleteAll();
        assertThat(bookRepository.count()).isEqualTo(0);
    }

Và mặc dù chúng ta đang sử dụng CrudRepository ,lưu ý rằng các phương thức tương tự tồn tại ở các Spring Data Jpa khác như JpaRepository hoặc PagingAndSortingRepository

4 Truy vấn xoá có dẫn xuất

Chúng tôi cũng có thể lấy các phương thức truy vấn để xóa các thực thể. Có một bộ quy tắc để viết chúng, nhưng chúng ta hãy chỉ tập trung vào ví dụ đơn giản nhất
Truy vấn xóa dẫn xuất phải bắt đầu bằng deleteBy, theo sau là tên của tiêu chí lựa chọn.
Các tiêu chí này phải được cung cấp trong lệnh gọi phương thức.
Giả sử chúng tôi muốn xoá Book theo Title . Sử dụng quy ước đặt tên ,chúng tôi sẽ bắt đầu với deleteBy và liệt kê title làm tiêu chí của chúng tôi:

@Repository
public interface BookRepository extends CrudRepository<Book,Long> {
    long deleteByTitle(String title);
}

Giá trị trả về là kiểu long chỉ ra số bản ghi đã bị xoá.
Hãy viết một hàm kiểm tra và đảm bảo điều đó đúng :

@Test
@Transactional
public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() {
        long deletedRecords = bookRepository.deleteByTitle("The Hobbit");
        assertThat(deletedRecords).isEqualTo(1);
    }

Thêm và xoá đối tượng trong JPA yêu cầu transaction . Đó là lý do tại sao chúng ta nên sử dụng annotation @Transactional khi sử dụng các truy vấn xoá có nguồn gốc này , để đảm bảo transaction đang chạy . Điều này sẽ được giải thích chi tiết trong tài liệu ORM với Spring

5. Custom câu lệnh truy vấn xoá

Tên phương thức cho các truy vấn dẫn xuất có thể khá dài và chúng chỉ giới hạn trong một bảng duy nhất
Khi chúng ta cần thứ gì đó phức tạp hơn, chúng ta có thể viết một truy vấn tùy chỉnh bằng cách sử dụng @Query và @Modify cùng nhau.

@Modifying
@Query("delete from Book b where b.title=:title")
void deleteBooks(@Param("title") String title);

Một lần nữa, chúng tôi có thể xác minh rằng nó hoạt động bằng một hàm kiểm tra đơn giản

@Test
@Transactional
public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() {
        bookRepository.deleteBooks("The Hobbit");
        assertThat(bookRepository.count()).isEqualTo(1);
    }

Cả hai giải pháp được trình bày ở trên là tương tự và đạt được cùng một kết quả. Tuy nhiên, họ có một cách tiếp cận hơi khác
Phương thức @Query tạo một truy vấn JPQL duy nhất dựa trên cơ sở dữ liệu. So sánh, các phương thức deleteBy thực hiện một truy vấn đọc và sau đó xóa từng mục một

6. Xoá trong các mỗi quan hệ

Bây giờ chúng ta hãy xem điều gì sẽ xảy ra khi chúng ta có mối quan hệ với các thực thể khác.
Giả sử chúng ta có thực thể Category có liên kết OneToMany với thực thể Book:

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Category {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
    private List<Book> books;

    public Category(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

Category chỉ có thể là 1 interface trống kế thừa CrudRepository

public interface CategoryRepository extends JpaRepository<Category,Long> {
}

Bây giờ chúng ta them 2 category và liên kết với những book mà chúng ta đã có ở trên :

   @ManyToOne
   private Category category;

Bây giờ nếu chúng tôi cố gắng xóa các Category, Book cũng sẽ bị xóa theo:

   @Test
    public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() {
        categoryRepository.deleteAll();
        assertThat(bookRepository.count()).isEqualTo(0);
        assertThat(categoryRepository.count()).isEqualTo(0);
    }

Đây không phải là mối quan hệ 2 chiều , có nghĩa nếu ta xoá hết Book , các Category vẫn còn ở đó.

  @Test
    public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() {
        bookRepository.deleteAll();
        assertThat(bookRepository.count()).isEqualTo(0);
        assertThat(categoryRepository.count()).isEqualTo(2);
    }

Chúng ta có thể thay đổi hành vi này bằng cách thay đổi các thuộc tính của mối quan hệ, chẳng hạn như CascadeType.

7.Tổng Kết

Trong bài viết này, chúng ta đã thấy các cách khác nhau để xóa các thực thể trong Spring Data JPA.
Chúng tôi đã xem xét các phương pháp xóa được cung cấp từ CrudRepository cũng như các truy vấn bắt nguồn của chúng tôi hoặc các truy vấn tùy chỉnh bằng cách sử dụng chú thích @Query.
Chúng tôi cũng đã thấy cách xóa được thực hiện trong các mối quan hệ.
Như mọi khi, tất cả các đoạn mã được đề cập trong bài viết này có thể được tìm thấy trên kho lưu trữ GitHub của chúng tôi

Bình luận

avatar
Nguyễn Quốc Thái 2022-05-25 14:22:59.511893 +0000 UTC

tuyệt vời

Avatar
* Vui lòng trước khi bình luận.
Ảnh đại diện
  +1 Thích
+1