[英]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
關系添加到Album
和Tag
。
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.