2016-07-27 17 views
2

私はPostTagの@manytomany関係を持つモデルを持っています。スプリングデータJPA Pageable with @ManyToMany

ポスト

@Entity 
public class Post { 
    private long id; 

    @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE }) 
    @JoinTable(joinColumns = @JoinColumn(name = "post_id"), inverseJoinColumns = @JoinColumn(name = "tag_id")) 
    private Set<Tag> tags; 

    ... 
} 

タグ

@Entity 
public class Tag { 
    private String name; 

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy = "tags") 
    private List<Post> posts = new ArrayList<Post>(); 

私はタグ名によって、すべてのページ番号付きの記事を見つけた方法をしたいのですが。

JPQLはLIMITをサポートしていません。

setFirstResult().setMaxResults().getResultList()を使用して自分のページングロジックを実装する必要がありますか?

@manytomanyのページ設定のベストプラクティスは何ですか?

私は少し質問を編集しました。下図のように私は私のコードを書いた:

@SuppressWarnings("unchecked") 
public Page<Post> findByTagName(String tagName, Pageable pageable) { 

    long total = (long) em 
      .createQuery("SELECT COUNT(p.id) FROM Post p JOIN p.tags t WHERE t.name = :tagName") 
      .setParameter("tagName", tagName) 
      .getSingleResult(); 

    List<Post> content = (List<Post>) em 
      .createQuery("SELECT p FROM Post p JOIN FETCH p.tags t WHERE t.name = :tagName") 
      .setParameter("tagName", tagName) 
      .setFirstResult(pageable.getOffset()) 
      .setMaxResults(pageable.getPageSize()) 
      .getResultList(); 

    PageImpl<Post> page = new PageImpl<Post>(content, pageable, total); 

    return page; 
} 

このコードは正常に動作しますが、これは正しい方法であれば、私はまだ思ったんだけど。

ありがとうございます。

+0

私はあなたが '私が使用したい[春-データ-JPA]' –

+0

@RobertNiestroj [春・データを使用している場所を確認いけません-jpa]リポジトリインタフェースメソッドの宣言ですが、limitとJOINを持つJPQLをサポートしているようには見えません。あるいは、[em]を使ってカスタムメソッドを実装しました。うまくいけば、再利用可能な[Page]型を返します。だから、私は正しいことをしているのだろうか。ありがとうございました。 – nasiajai

答えて

0

それはあなたのケースで動作しますが、また、このリンクをチェックし

setMaxResults for Spring-Data-JPA annotation?

場合は、春のJPAに制限のためのFindFirstまたはfindTop方法を使用することができますわかりません。

http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result

+0

Pageable、firstまたはtopを "single"エンティティで使用する方法を知っています。しかし、それは多対多の関係をサポートしていないようです。したがって、私は自分の質問を編集し、いくつかのコードを追加しました。あなたの意見を教えてください。ありがとうございました。 – nasiajai

1

ページの操作と@ManyToManyマッピングは本当に簡単な作業です。

最初にここにあなたのモデルと類似したモデルがあります(基本的には@Id@GeneratedValueというアノテーションが追加されてデータベース識別子が生成されます)。

ポストエンティティ:

package com.example.model; 

import java.util.HashSet; 
import java.util.Set; 

import javax.persistence.CascadeType; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.JoinColumn; 
import javax.persistence.JoinTable; 
import javax.persistence.ManyToMany; 

@Entity 
public class Post { 

    @Id 
    @GeneratedValue 
    private long id; 

    @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST, CascadeType.MERGE }) 
    @JoinTable(joinColumns = @JoinColumn(name = "post_id"), inverseJoinColumns = @JoinColumn(name = "tag_id")) 
    private Set<Tag> tags = new HashSet<>(); 

    public Set<Tag> getTags() { 
     return tags; 
    } 

} 

タグエンティティ:

package com.example.model; 

import java.util.ArrayList; 
import java.util.List; 

import javax.persistence.CascadeType; 
import javax.persistence.Entity; 
import javax.persistence.FetchType; 
import javax.persistence.GeneratedValue; 
import javax.persistence.Id; 
import javax.persistence.ManyToMany; 

@Entity 
public class Tag { 

    @Id 
    @GeneratedValue 
    private long id; 

    private String name; 

    @ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE }, mappedBy = "tags") 
    private List<Post> posts = new ArrayList<Post>(); 

    public void setName(String name) { 
     this.name = name; 
    } 

} 

今、あなたはポストエンティティをフェッチするPagingAndSortingRepositoryが必要になります。pagablesでの作業

package com.example.repository; 

import java.util.Set; 

import org.springframework.data.domain.Page; 
import org.springframework.data.domain.Pageable; 
import org.springframework.data.repository.PagingAndSortingRepository; 
import org.springframework.stereotype.Repository; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.Post; 

@Repository 
public interface PostRepository extends PagingAndSortingRepository<Post, Long> { 

    @Transactional(readOnly = true) 
    Set<Post> findByTagsName(String name); 

    @Transactional(readOnly = true) 
    Page<Post> findByTagsName(String name, Pageable pageable); 

} 

は同じくらい簡単です。通常のSpring Data JPAファインダメソッドを記述します。割り当てられたタグエンティティの名前で投稿を検索する場合は、findBy Tags + Nameなどのフィールド名を連鎖させて、通常のファインダを作成してください。これにより、JPQLのアプローチSELECT p FROM Post p JOIN FETCH p.tags t WHERE t.name = :tagNameに似たクエリが作成されます。タグ名のパラメータをメソッドのパラメータとして渡します。

Pageableサポートを追加する場合は、Pageableというパラメータを2番目のパラメータとして追加し、戻り値をSetの代わりにPageに変更します。それで全部です。少なくともここ

は、コードを確認するためにいくつかのテストは以下のとおりです。

package com.example.repository; 

import static org.hamcrest.CoreMatchers.is; 
import static org.hamcrest.Matchers.empty; 
import static org.hamcrest.Matchers.hasSize; 
import static org.junit.Assert.assertThat; 

import java.util.Set; 

import javax.persistence.EntityManager; 
import javax.persistence.PersistenceContext; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.data.domain.Page; 
import org.springframework.data.domain.PageRequest; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.Post; 
import com.example.model.Tag; 

@RunWith(SpringRunner.class) 
@Transactional 
@SpringBootTest 
public class PostRepositoryTests { 

    @Autowired 
    private PostRepository postRepository; 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Test 
    public void receiveMultiplePostsWithTagsByName() { 
     final String nameA = "A"; 
     final String nameB = "B"; 
     final String nameC = "C"; 
     final String nameD = "D"; 
     final String nameE = "E"; 

     final Tag tagA = new Tag(); 
     tagA.setName(nameA); 
     final Tag tagB = new Tag(); 
     tagB.setName(nameB); 
     final Tag tagC = new Tag(); 
     tagC.setName(nameC); 
     final Tag tagD = new Tag(); 
     tagD.setName(nameD); 
     final Tag tagE = new Tag(); 
     tagE.setName(nameE); 

     final Post postOne = new Post(); 
     postOne.getTags().add(tagA); 
     postOne.getTags().add(tagB); 
     postRepository.save(postOne); 

     final Post postTwo = new Post(); 
     postTwo.getTags().add(tagA); 
     postTwo.getTags().add(tagB); 
     postTwo.getTags().add(tagE); 
     postRepository.save(postTwo); 

     final Post postThree = new Post(); 
     postThree.getTags().add(tagA); 
     postThree.getTags().add(tagB); 
     postThree.getTags().add(tagC); 
     postThree.getTags().add(tagE); 
     postRepository.save(postThree); 

     entityManager.flush(); 
     entityManager.clear(); 

     final Set<Post> tagsByA = postRepository.findByTagsName(nameA); 
     assertThat("Expected three hits!", tagsByA, hasSize(3)); 

     final Set<Post> tagsByB = postRepository.findByTagsName(nameB); 
     assertThat("Expected three hits!", tagsByB, hasSize(3)); 

     final Set<Post> tagsByC = postRepository.findByTagsName(nameC); 
     assertThat("Expected one hit!", tagsByC, hasSize(1)); 

     final Set<Post> tagsByD = postRepository.findByTagsName(nameD); 
     assertThat("Expected no hits!", tagsByD, empty()); 

     final Set<Post> tagsByE = postRepository.findByTagsName(nameE); 
     assertThat("Expected two hits!", tagsByE, hasSize(2)); 
    } 

    @Test 
    public void receiveMultiplePostsWithTagsByNamePaged() { 
     final String nameA = "A"; 

     final Tag tagA = new Tag(); 
     tagA.setName(nameA); 

     final Post postOne = new Post(); 
     postOne.getTags().add(tagA); 
     postRepository.save(postOne); 

     final Post postTwo = new Post(); 
     postTwo.getTags().add(tagA); 
     postRepository.save(postTwo); 

     final Post postThree = new Post(); 
     postThree.getTags().add(tagA); 
     postRepository.save(postThree); 

     final Post postFour = new Post(); 
     postFour.getTags().add(tagA); 
     postRepository.save(postFour); 

     final Post postFive = new Post(); 
     postFive.getTags().add(tagA); 
     postRepository.save(postFive); 

     entityManager.flush(); 
     entityManager.clear(); 

     final Page<Post> tagsByAFirstPageSize2 = postRepository.findByTagsName(nameA, new PageRequest(0, 2)); 
     assertThat("Expected two page items!", tagsByAFirstPageSize2.getContent(), hasSize(2)); 
     assertThat("Expected five items in sum!", tagsByAFirstPageSize2.getTotalElements(), is(5L)); 
     assertThat("Should be first page!", tagsByAFirstPageSize2.isFirst(), is(true)); 
     assertThat("Should not be last page!", tagsByAFirstPageSize2.isLast(), is(false)); 

     final Page<Post> tagsBySecondPageSize2 = postRepository.findByTagsName(nameA, new PageRequest(1, 2)); 
     assertThat("Expected two page items!", tagsBySecondPageSize2.getContent(), hasSize(2)); 
     assertThat("Expected five items in sum!", tagsBySecondPageSize2.getTotalElements(), is(5L)); 
     assertThat("Should not be first page!", tagsBySecondPageSize2.isFirst(), is(false)); 
     assertThat("Should not be last page!", tagsBySecondPageSize2.isLast(), is(false)); 

     final Page<Post> tagsByLastPageSize2 = postRepository.findByTagsName(nameA, new PageRequest(2, 2)); 
     assertThat("Expected one last page item!", tagsByLastPageSize2.getContent(), hasSize(1)); 
     assertThat("Expected five items in sum!", tagsByLastPageSize2.getTotalElements(), is(5L)); 
     assertThat("Should not be first page!", tagsByLastPageSize2.isFirst(), is(false)); 
     assertThat("Should be last page!", tagsByLastPageSize2.isLast(), is(true)); 
    } 

} 
関連する問題