我有三个课程,如下所述。我正在尝试创建一个规范来过滤链接表中匹配的数据。
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);
}
}
答案 0 :(得分:2)
使用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.
我希望这会帮助你解决它。
答案 1 :(得分:1)
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
)
答案 2 :(得分:1)
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));
}
答案 3 :(得分:1)
嗯,在这种情况下我不会进行in
操作 - 它只会使查询和规范复杂化。您描述的问题实际上是将Table A
中的记录与来自Table B
的相关记录相关联,因此您的案例中的查询将如下:
SELECT a from Album a join AlbumTag at on a.id = at.albumId
- 根据需要,它会返回所有包含相册标签的相册。 Inner join explained
所以在你的情况下,我会创建这个&#34;工厂&#34;将为您创建此规范的方法。
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
库 - link to introduction。它为您生成实体类中的静态模型,帮助您避免使用硬编码字符串创建查询/规范。
答案 4 :(得分:1)
这看起来像一个经典的很多例子。您已将这三个类直接映射到数据库中预期的表。 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
查找相册,您首先应使用Tag
或findById(1l);
之类的内容从存储库中检索findByCategory("Rock");
,然后只需在getAlbums()
上调用Tag
$(window).on('load', function (e) { line
对象。
注意:这里的一个细微差别是AlbumTag表只有两列(专辑和标签)。 AlbumTag上的额外id列是不必要的,因为专辑和标签的组合将是唯一的ID,无论如何你永远不需要在这个表中找到id。
答案 5 :(得分:1)
由于您使用的是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();
}
然后只需调用存储库函数,该函数将返回至少包含一个标记的相册列表。