Все пытаюсь научиться эффективно работать с JPA, да выходит не очень =)
У меня есть такая вот модель:
@Entity
public class Organization extends AbstractIdentifiable implements Serializable {
@Column(nullable = false)
private String title;
@ManyToMany(mappedBy = "organizations")
private Set<Member> members = new HashSet<>();
@ManyToMany(mappedBy = "customerOrganization")
private Set<Video> videos = new HashSet<>();
@ManyToOne
@JoinColumn
private Playlist playlist;
}
@Entity
public class Video extends AbstractIdentifiable implements Serializable {
@ManyToOne
private Organization customerOrganization;
@OneToMany(mappedBy = "video")
private Set<ShownVideo> shownVideos = new HashSet<>();
}
@Entity
public class ShownVideo extends AbstractIdentifiable implements Serializable {
@ManyToOne
@JoinColumn(nullable = false)
private Video video;
}
Весь не относящийся к делу код выпилен. Я хочу получить по каждой организации число просмотров видео, чтобы в результирующий список попали только те организации, в которых есть заданный member.
Данную задачу решаю с помощью spring-data:
public interface OrganizationRepository extends JpaRepository<Organization, Long> {
@Query("SELECT new OrganizationCount(o, COUNT(sv)) " +
"FROM Organization o JOIN o.members m " +
"LEFT JOIN o.videos v " +
"LEFT JOIN v.shownVideos sv " +
"WHERE m = :member " +
"GROUP BY o " +
"ORDER BY o.title")
public List<OrganizationCount> organizationVideoShowCountsByMember(@Param(value = "member") Member member);
}
public class OrganizationCount {
private Organization organization;
private long count;
public OrganizationCount(Organization organization, long count) {
this.organization = organization;
this.count = count;
}
public Organization getOrganization() {
return organization;
}
public long getCount() {
return count;
}
}
Данная задача решена, но очень неэффективно. Сначала выполняется корректный запрос на получение статистики:
Hibernate: select organizati0_.id as col_0_0_, count(shownvideo3_.id) as col_1_0_ from Organization organizati0_ left outer join Video videos1_ on organizati0_.id=videos1_.customerOrganization_id left outer join Video video2_ on videos1_.id=video2_.id left outer join shown_video shownvideo3_ on video2_.id=shownvideo3_.video_id group by organizati0_.id order by organizati0_.title
Hibernate: select organizati0_.id as id1_3_0_, organizati0_.playlist_id as playlist3_3_0_, organizati0_.title as title2_3_0_, playlist1_.id as id1_5_1_, playlist1_.title as title2_5_1_ from Organization organizati0_ left outer join Playlist playlist1_ on organizati0_.playlist_id=playlist1_.id where organizati0_.id=?
Hibernate: select organizati0_.id as id1_3_0_, organizati0_.playlist_id as playlist3_3_0_, organizati0_.title as title2_3_0_, playlist1_.id as id1_5_1_, playlist1_.title as title2_5_1_ from Organization organizati0_ left outer join Playlist playlist1_ on organizati0_.playlist_id=playlist1_.id where organizati0_.id=?
Дополнено: Если вместо
SELECT new com.helan.adkiosk.webserver.controllers.data.aggreate.OrganizationCount(o, COUNT(sv))"
SELECT o, COUNT(sv)
Hibernate: select organizati0_.id as col_0_0_, count(shownvideo3_.id) as col_1_0_, organizati0_.id as id1_3_, organizati0_.playlist_id as playlist3_3_, organizati0_.title as title2_3_ from Organization organizati0_ left outer join Video videos1_ on organizati0_.id=videos1_.customerOrganization_id left outer join Video video2_ on videos1_.id=video2_.id left outer join shown_video shownvideo3_ on video2_.id=shownvideo3_.video_id group by organizati0_.id order by organizati0_.title
Hibernate: select playlist0_.id as id1_5_0_, playlist0_.title as title2_5_0_ from Playlist playlist0_ where playlist0_.id=?
То есть первым запросом выбираются сразу и организации и статистика по ним. Только обращаться к результирующим данным приходится через список списков, что неудобно, ибо дальше этот объект передается в шаблонизатор.
Но и тут косяк, ибо потом для организаций, для которых имеются плейлисты производится выборка этих плейлистов. Они мне в принципе не нужны в том месте и их можно было бы не выбирать вовсе. В сущности Organization
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Playlist playlist;