Java 将使用多个关系的SQL查询转换为JPQL查询

Java 将使用多个关系的SQL查询转换为JPQL查询,java,spring,hibernate,spring-boot,jpa,Java,Spring,Hibernate,Spring Boot,Jpa,我正在尝试将以下SQL查询转换为JPQL查询: SELECT * FROM movie m INNER JOIN movie_genre mg ON m.id = mg.movie_id WHERE mg.genre_id = (SELECT mg2.genre_id FROM movie_genre mg2 WHERE mg2.movie_id = ?1 AND mg2.movie_id <> mg.movie_id AND mg.genre_id = mg2.genre_id)

我正在尝试将以下SQL查询转换为JPQL查询:

SELECT *
FROM movie m INNER JOIN movie_genre mg ON m.id = mg.movie_id
WHERE mg.genre_id = (SELECT mg2.genre_id FROM movie_genre mg2 WHERE mg2.movie_id = ?1 AND mg2.movie_id <> mg.movie_id AND mg.genre_id = mg2.genre_id)
GROUP BY mg.movie_id ORDER BY count(*) DESC
编辑: 以下是模型:
电影

@Entity
public class Movie extends DateAudit  {

    private Long id;
    private String name;
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
     @JoinTable(name = "movie_genre",
        joinColumns = @JoinColumn(name = "movie_id"),
        inverseJoinColumns = @JoinColumn(name = "genre_id")
    )
    private List<Genre> genres = new ArrayList<>();
}
@Entity
public class Genre {

    private Long id;
    private String name;
    @ManyToMany(mappedBy = "genres", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Set<Movie> movies = new HashSet<>();
}
@实体
公共类审核{
私人长id;
私有字符串名称;
@ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE})
@JoinTable(name=“电影类型”,
joinColumns=@JoinColumn(name=“movie\u id”),
inverseJoinColumns=@JoinColumn(name=“genre\u id”)
)
私有列表类型=新的ArrayList();
}
流派

@Entity
public class Movie extends DateAudit  {

    private Long id;
    private String name;
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
     @JoinTable(name = "movie_genre",
        joinColumns = @JoinColumn(name = "movie_id"),
        inverseJoinColumns = @JoinColumn(name = "genre_id")
    )
    private List<Genre> genres = new ArrayList<>();
}
@Entity
public class Genre {

    private Long id;
    private String name;
    @ManyToMany(mappedBy = "genres", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    private Set<Movie> movies = new HashSet<>();
}
@实体
公共课体裁{
私人长id;
私有字符串名称;
@ManyToMany(mappedBy=“genres”,cascade={CascadeType.PERSIST,CascadeType.MERGE})
private Set movies=new HashSet();
}

编写JPQL语句不需要模型电影类型。Hibernate在执行JPQL语句时隐式地知道它

例如:

SELECT m from Movie m left join m.genres g where g.name = 'Action'
双向“m.genres g”适用于所有双向JPQL语句,包括多对多,其中关联模型是隐式的,实际上并不存在

示例代码:

@Entity
@Table(name = "MOVIE")
public class Movie {

@Id
@GeneratedValue
private Long id;

@Column
private String name;

@ManyToMany(cascade = {CascadeType.ALL})
@JoinTable(name = "movie_genre",
        joinColumns = @JoinColumn(name = "movie_id"),
        inverseJoinColumns = {@JoinColumn(name = "genre_id")}
)
private Set<Genre> genres = new HashSet<>();

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}


public Set<Genre> getGenres() {
    return genres;
}

public void setGenres(Set<Genre> genres) {
    this.genres = genres;
}
}

@Entity
@Table(name = "GENRE")
public class Genre {

@Id
@GeneratedValue
private Long id;

@Column
private String name;


@ManyToMany(mappedBy = "genres", cascade = {CascadeType.ALL})
private Set<Movie> movies = new HashSet<>();

public Long getId() {
    return id;
}

public void setId(Long id) {
    this.id = id;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public Set<Movie> getMovies() {
    return movies;
}

public void setMovies(Set<Movie> movies) {
    this.movies = movies;
}
}
@实体
@表(name=“MOVIE”)
公映{
@身份证
@生成值
私人长id;
@纵队
私有字符串名称;
@ManyToMany(cascade={CascadeType.ALL})
@JoinTable(name=“电影类型”,
joinColumns=@JoinColumn(name=“movie\u id”),
inverseJoinColumns={@JoinColumn(name=“genre_id”)}
)
私有集类型=新HashSet();
公共长getId(){
返回id;
}
公共无效集合id(长id){
this.id=id;
}
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共集getGenres(){
回归体裁;
}
公共无效集合类型(集合类型){
this.genres=流派;
}
}
@实体
@表(name=“GENRE”)
公共课体裁{
@身份证
@生成值
私人长id;
@纵队
私有字符串名称;
@ManyToMany(mappedBy=“genres”,cascade={CascadeType.ALL})
private Set movies=new HashSet();
公共长getId(){
返回id;
}
公共无效集合id(长id){
this.id=id;
}
公共字符串getName(){
返回名称;
}
公共void集合名(字符串名){
this.name=名称;
}
公共集getMovies(){
返回电影;
}
公共电影集(电影集){
这部电影=电影;
}
}
工作JPQL示例

    @PersistenceContext
EntityManager entityManager;

@Test
@Transactional
public void test() {
    Set<Movie> actionMovies = new HashSet<>();
    Set<Movie> dramaMovies = new HashSet<>();
    Set<Genre> dramaGenres = new HashSet<>();
    Set<Genre> actionGenres = new HashSet<>();
    Set<Genre> generes = new HashSet<>();

    Movie actionMovie = new Movie();
    actionMovie.setName("Batman");
    actionMovies.add(actionMovie);

    Movie dramaMovie = new Movie();
    dramaMovie.setName("Forest Gump");
    dramaMovies.add(dramaMovie);

    Genre actionGenre = new Genre();
    actionGenre.setName("Action");
    generes.add(actionGenre);
    actionGenres.add(actionGenre);

    Genre dramaGenre = new Genre();
    dramaGenre.setName("Drama");
    generes.add(dramaGenre);
    dramaGenres.add(dramaGenre);

    //Bidirectional sets
    actionGenre.setMovies(actionMovies);
    dramaGenre.setMovies(dramaMovies);
    actionMovie.setGenres(actionGenres);
    dramaMovie.setGenres(dramaGenres);

    genreRepository.saveAll(generes);

    //Example JPQL join through not present association model.
    Query query = entityManager.createQuery("SELECT m from Movie m left join m.genres g where g.name = 'Action'");
    List<Movie> resultList = query.getResultList();
    assertEquals("Batman",resultList.get(0).getName());


}
@PersistenceContext
实体管理器实体管理器;
@试验
@交易的
公开无效测试(){
Set actionMovies=newhashset();
Set=new HashSet();
Set-tramagenres=new-HashSet();
Set actionGenres=new HashSet();
Set genres=new HashSet();
电影动作电影=新电影();
动作片。集名(“蝙蝠侠”);
添加(动作电影);
电影剧本电影=新电影();
戏剧电影集名称(“森林甘普”);
添加(戏剧电影);
流派动作流派=新流派();
actionGenre.setName(“动作”);
添加(动作类型);
动作类型。添加(动作类型);
体裁戏剧体裁=新体裁();
戏剧类型。设置名称(“戏剧”);
增加(戏剧体裁);
戏剧流派。添加(戏剧流派);
//双向集合
动作类型:动作电影;
戏剧类型:情景电影(戏剧电影);
动作电影。动作类型(动作类型);
戏剧电影。场景类型(戏剧类型);
genreRepository.saveAll(genes);
//示例JPQL通过不存在关联模型连接。
Query Query=entityManager.createQuery(“从电影m中选择m left join m.genres g,其中g.name='Action'”);
List resultList=query.getResultList();
assertEquals(“蝙蝠侠”,resultList.get(0.getName());
}

尽管您提供了SQL查询,我还是重新考虑了您的需求,并将其转换为JPQL。到目前为止,我所理解的是,你在寻找与一部具有共同类型的电影相关的电影。如果这是正确的,您可以通过

SELECT m FROM Movie m JOIN m.genres mg 
WHERE mg.id IN 
         (SELECT g.id FROM Genre g JOIN g.movies gm WHERE gm.id = :movieId) 
AND m.id <> :movieId 
GROUP BY m ORDER BY count(*) DESC
性能问题 正如您在生成的SQL中所注意到的,有许多级别的联接来获取和过滤数据。这会导致性能瓶颈。要克服这一点,您可以为
电影类型
表设置实体

这里公平地讨论了这个场景


除了映射之外,
movie\u genre
表是否还包含其他属性?@ShafinMahmud不仅
id\u movie
id\u genre
有双向关系映射吗<代码>电影包含<代码>流派列表和<代码>流派包含<代码>电影列表?如果您提到您的实体映射,那就太好了。@ShafinMahmud是的,我有一个双向关系。请参阅我的编辑。
 SELECT m FROM Movie m JOIN m.genres mg WHERE mg.id = :genreId