簡體   English   中英

多個到多個的JPA Criteria API規范

[英]JPA Criteria API Specification for Many to Many

我有三個課程,如下所述。 我正在嘗試創建一個規范來過濾鏈接表中匹配的數據。

public class Album {
    private Long id;
    private List<AlbumTag> albumTags;
}

public class Tag {
    private Long id;
    private String category;
}

public class AlbumTag{
    private Long id;
    private Album album;
    private Tag tag;
}

在上面給出的模式中,我想要找到的是Album表中所有相冊的列表以及AlbumTag中的鏈接。 我想要實現的SQL不必相同,如下所示

select *
from Album A 
where (A.Id in (select [AT].AlbumId 
from AlbumTag [AT]))

到目前為止,我所嘗試的當時沒有工作的是下面的內容

public class AlbumWithTagSpecification implements Specification<Album> {

    @Override
    public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

         final Subquery<Long> personQuery = cq.subquery(Long.class); 
         final Root<Album> album = personQuery.from(Album.class); 
         final Join<Album, AlbumTag> albumTags = album.join("albumTags");
         personQuery.select((albumTags.get("album")).get("id"));
         personQuery.where(cb.equal(album.get("id"), (albumTags.get("album")).get("id"))); 
         return cb.in(root.get("id")).value(personQuery);

    }
}

使用spring boot和spring數據JPA,您可以更喜歡實體關系來獲取數據。

1.使用下面給出的實體關系來注釋域類:

@Entity
@Table(name="Album")
public class Album {
    @Id
    @Column(name="id")
    private Long id;
    @OneToMany(targetEntity = AlbumTag.class, mappedBy = "album")
    private List<AlbumTag> albumTags;

    //getter and setter
}

@Entity
@Table(name="Tag")
public class Tag {
    @Id
    @Column(name="id")
    private Long id;
    @Column(name="category")
    private String category;

    //getter and setter
}

@Entity
@Table(name="AlbumTag")
public class AlbumTag{
    @Id
    @Column(name="id")
    private Long id;
    @ManyToOne(optional = false, targetEntity = Album.class)
    @JoinColumn(name = "id", referencedColumnName="id", insertable = false, updatable = false)
    private Album album;
    @ManyToOne(optional = false, targetEntity = Tag.class)
    @JoinColumn(name = "id", referencedColumnName="id", insertable = false, updatable = false)
    private Tag tag;

    //getter and setter
}

2.使用彈簧數據獲取詳細信息,如下所示:

Album album = ablumRepository.findOne(1); // get the complete details about individual album.
List<AlbumTag> albumTags = ablum.getAlbumTags(); // get the all related albumTags details for particular album.

我希望這能幫助你解決它。

JPA中的子查詢只能與CriteriaBuilder.exists()一起使用,所以我會嘗試:

public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

     final Subquery<Long> subQuery = cq.subquery(Long.class); 
     final Root<AlbumTag> albumTag = subQuery.from(AlbumTag.class); 
     // it doesn't really matter what we select
     subQuery.select(cb.literal(1));
     subQuery.where(cb.equal(root.get("id"), (albumTag.get("album")).get("id"))); 

     return cb.exists(subQuery);

}

這相當於

select *
from Album A 
where exists(
    select 1 from AlbumTag AT 
    where AT.AlbumId = A.Id
)
creteria query for join tables

CriteriaQuery<Album> query = cb.createQuery(Album.class);
Root<Album> album = query.from(Teacher.class);
Join<Album, AlbumTag> tag = teacher.join("id");
query.select(tag).where(cb.equal(album.get("album")));

List<Album> results = em.createQuery(query).getResultList();
for (Album al : results) {
    System.out.println("album-->+al.get(name));
}

好了,我不會去in運行在這種情況下-它只是查詢和規范復雜化。 您描述的問題實際上是將Table A記錄與Table B相關記錄相關聯,因此您的案例中的查詢將如下所示:

SELECT a from Album a join AlbumTag at on a.id = at.albumId - 根據需要,它將返回所有具有相冊標簽的相冊。 內部聯接解釋

所以在你的情況下,我會創建這個“工廠”方法,為你創建這個規范。

public static Specification<Album> withTags() {
    return new Specification<Album>() {
        @Override
        public Predicate toPredicate(Root<Album> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return root.join("albumTags").getOn();
    }
};

}

另外我建議你看看來自hibernate的static metamodel庫 - 鏈接到介紹 它為您生成實體類中的靜態模型,幫助您避免使用硬編碼字符串創建查詢/規范。

這看起來像一個經典的很多例子。 您已將這三個類直接映射到數據庫中預期的表。 JPA是一個對象關系映射(ORM)庫,這意味着我們可以用更OO樣式構造類並映射到底層關系數據庫。

可以省略AlbumTag類,並將@ManyToMany關系添加到AlbumTag

public class Album {
    private Long id;

    @ManyToMany
    @JoinTable(name="AlbumTag",
        joinColumns=
            @JoinColumn(name="album", referencedColumnName="id"),
        inverseJoinColumns=
            @JoinColumn(name="tag", referencedColumnName="id"))
    private List<Tag> tags;
}

public class Tag {
    private Long id;
    private String category;

    @ManyToMany(mappedBy="tags")
    private List<Album> albums;
}

要通過Tag查找專輯,您首先要使用findById(1l);findById(1l);從存儲庫中檢索Tag findById(1l); 或者findByCategory("Rock"); 然后只需在Tag對象上調用getAlbums()

注意:這里的一個細微差別是AlbumTag表只有兩列(專輯和標簽)。 AlbumTag上的額外id列是不必要的,因為專輯和標簽的組合將是唯一的ID,無論如何你永遠不需要在這個表中找到id。

由於您使用的是spring-data-jpa,因此您應該充分利用它提供的功能。

我的第一個問題與您的實體類有關。 我不明白為什么在專輯類中存儲專輯標簽列表是必要的。 由於您有一個連接表,因此該信息是可還原的。

其次,您應該注釋您的實體條款:

@Entity
public class Album {
@Id
@Column
private Long id;
}

@Entity
public class Tag {
  @Id
  @Column
  private Long id;
  @Column
  private String category;
}

@Entity
@Table
public class AlbumTag{
  @Id
  @Column
  private Long id;
  @ManyToOne
  @JoinColumn
  private Album album;
  @ManyToOne
  @JoinColumn
  private Tag tag;
}

接下來,您應該為實體類創建存儲庫。

interface AlbumRepository extends JpaRepository<Album, Long>{

   @Query
   ("select DISTINCT(a) from AlbumTag at "+
    "join at.album a "
    "where at.tag is not null")
   List<Album> findAlbumWithTag();
}

然后只需調用存儲庫函數,該函數將返回至少包含一個標記的相冊列表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM