2016-07-27 159 views
2

我有PostTag模型有@manytomany關係。@ManyToMany Spring Data JPA Pageable

@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]存儲庫接口方法聲明,但它看起來不像它支持限制和JOIN的JPQL。或者,我使用[em]實現了自定義方法。希望它返回可重用的[Page]類型。所以,我想知道我做對了。謝謝。 – nasiajai

回答

0

不知道它在你的情況,但檢查此鏈接

setMaxResults for Spring-Data-JPA annotation?

此外,

可以使用的FindFirst或findTop方法限制在春天JPA。

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

+0

我知道如何將Pageable,first或top用於「單個」實體。但是,它似乎並不支持多對多的關係。因此,我編輯了我的問題,並附加了一些代碼。請讓我知道你的意見。謝謝。 – 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用於提取後的實體:

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); 

} 

與pagables工作是幾乎一樣簡單定期編寫Spring Data JPA查找方法。如果你想找到指定的標籤實體的名字帖子鏈接像findBy Tags + Name字段名稱只寫普通取景器。這會創建一個類似於您的JPQL方法SELECT p FROM Post p JOIN FETCH p.tags t WHERE t.name = :tagName的查詢。將標記名稱的參數僅作爲方法參數傳遞。

現在 - 如果要添加Pageable支持 - 只需添加類型爲Pageable的參數作爲第二個參數,並將返回值轉換爲Page而不是Set。就這樣。

至少這裏有一些測試,以驗證代碼:

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)); 
    } 

}