Spring Data JPAの削除と関係

1. 概要

このチュートリアルでは、https://spring.io/projects/spring-data-jpa [Spring Data JPA]で削除がどのように行われるかを見ていきます。

2. サンプルエンティティ

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories[Spring Data JPAリファレンスドキュメント]からわかるように、リポジトリインターフェイスはエンティティの基本的なサポートを提供します。
_Book_のようなエンティティがある場合:
@Entity
public class Book {

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

    // standard constructors

    // standard getters and setters
}
次に、Spring Data JPAの_CrudRepository_を拡張して、_Book_のCRUD操作にアクセスできます。
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {}

3. リポジトリから削除

とりわけ、_CrudRepository_には、_deleteById_と_deleteAll_の2つのメソッドが含まれています。
これらのメソッドを_BookRepository_から直接テストしてみましょう。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class DeleteFromRepositoryUnitTest {

    @Autowired
    private BookRepository repository;

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

    // data initialization

    @Test
    public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() {
        repository.deleteById(book1.getId());
        assertThat(repository.count()).isEqualTo(1);
    }

    @Test
    public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() {
        repository.deleteAll();
        assertThat(repository.count()).isEqualTo(0);
    }
}
_CrudRepository_を使用している場合でも、_JpaRepository_や_PagingAndSortingRepository._などの他のSpring Data JPAインターフェースにも同じメソッドが存在することに注意してください。

4. 派生削除クエリ

エンティティを削除するためのクエリメソッドを派生させることもできます。 それらを記述するための一連のルールがありますが、最も単純な例に焦点を当てましょう。
*派生削除クエリは、_deleteBy_で始まり、その後に選択基準の名前が続く必要があります。*これらの基準は、メソッド呼び出しで提供する必要があります。
__Book__s by _title_を削除したいとしましょう。 命名規則を使用して、基準として_deleteBy_から始め、_title_をリストします。
@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    long deleteByTitle(String title);
}
タイプ_long_の戻り値は、メソッドが削除したレコードの数を示します。
テストを書き、それが正しいことを確認しましょう:
@Test
@Transactional
public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() {
    long deletedRecords = repository.deleteByTitle("The Hobbit");
    assertThat(deletedRecords).isEqualTo(1);
}
  • JPAでオブジェクトを永続化および削除するにはトランザクションが必要です。そのため、これらのlink:/spring-data-jpa-deleteby [派生削除クエリ]を使用する場合は、_ @ Transactional_アノテーションを使用する必要があります。トランザクションが実行されています。*これについては、https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#orm [SpringドキュメントのORM]で詳しく説明されています。

5. カスタム削除クエリ

派生クエリのメソッド名は非常に長くなる可能性があり、単一のテーブルに制限されます。
もっと複雑なものが必要な場合は、_ @ Query_と_https://www.baeldung.com/spring-data-jpa-modifying-annotation [@Modifying] _を一緒に使用してカスタムクエリを作成できます。
以前の派生メソッドの同等のコードを確認しましょう。
@Modifying
@Query("delete from Book b where b.title=:title")
void deleteBooks(@Param("title") String title);
繰り返しますが、単純なテストで機能することを確認できます。
@Test
@Transactional
public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() {
    repository.deleteBooks("The Hobbit");
    assertThat(repository.count()).isEqualTo(1);
}
上記の両方のソリューションは類似しており、同じ結果を達成します。 ただし、それらは少し異なるアプローチを取ります。
  • _ @ Query_メソッドは、データベースに対して単一のJPQLクエリを作成します。 比較すると、_deleteBy_メソッドは読み取りクエリを実行し、各アイテムを1つずつ削除します。*

6. 関係で削除

*他のエンティティとの関係があるときに何が起こるか見てみましょう*。
_Book_エンティティと_OneToMany_の関連付けを持つ_Category_エンティティがあるとします。
@Entity
public class Category {

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

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

    // standard constructors

    // standard getters and setters
}
_CategoryRepository_は、_CrudRepository_を拡張する空のインターフェイスにすることができます。
@Repository
public interface CategoryRepository extends CrudRepository<Category, Long> {}
この関連付けを反映するように_Book_エンティティも変更する必要があります。
@ManyToOne
private Category category;
2つのカテゴリを追加して、それらを現在所有している書籍に関連付けましょう。 *今、カテゴリを削除しようとすると、本も削除されます:*
@Test
public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() {
    categoryRepository.deleteAll();
    assertThat(bookRepository.count()).isEqualTo(0);
    assertThat(categoryRepository.count()).isEqualTo(0);
}
*ただし、これは双方向ではありません。*つまり、書籍を削除しても、カテゴリは引き続き存在します。
@Test
public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() {
    bookRepository.deleteAll();
    assertThat(bookRepository.count()).isEqualTo(0);
    assertThat(categoryRepository.count()).isEqualTo(2);
}
link:/delete-with-hibernate[_CascadeType_]などの関係のプロパティを変更することにより、この動作を変更できます。

7. 結論

この記事では、Spring Data JPAでエンティティを削除するさまざまな方法を検討しました。 提供された_CrudRepository_の削除メソッド、および派生クエリまたは_ @ Query_アノテーションを使用したカスタムクエリを確認しました。
リレーションシップで削除がどのように行われるかについても見てきました。 いつものように、この記事で言及したすべてのコードスニペットは、https://github.com/eugenp/tutorials/tree/master/persistence-modules/spring-data-jpa-2 [GitHubリポジトリ]にあります。