Конвертировать SQL-запрос, использующий отношение ManyToMany, в JPQL-запрос - PullRequest
0 голосов
/ 20 сентября 2018

Я пытаюсь преобразовать следующий запрос 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

Проблема в том, что у меня нет класса модели для представления таблицы movie_genre, потому что это автоматически сгенерированная таблица изManyToMany отношение.

, поэтому есть ли способ преобразовать этот запрос в JPQL

Сейчас я использую собственный запрос:

@Query(value = "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", nativeQuery = true)
Page<Movie> findRelatedMoviesToAMovieById(@Param("id") int id, Pageable pageable);

Редактировать: вот модели:
Фильм

@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<>();
}

Ответы [ 2 ]

0 голосов
/ 20 сентября 2018

Несмотря на предоставленный вами 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, как показано ниже

 select distinct movie0_.id as id1_1_, movie0_.name as name2_1_ 
 from movie movie0_ inner join movie_genre genres1_ on movie0_.id=genres1_.movie_id inner join genre genre2_ on genres1_.genre_id=genre2_.id 
 where (genre2_.id in 
            (select genre3_.id from genre genre3_ inner join movie_genre movies4_ on genre3_.id=movies4_.genre_id inner join movie movie5_
             on  movies4_.movie_id=movie5_.id where movie5_.id=?)) 
 and movie0_.id <> ? 
 group by movie0_.id order by count(*) desc

Запрос ассоциации коллекций в JPQL

Когда вы используетеJPQL, вы находитесь в мире объектных отношений.Поэтому, когда вы запрашиваете ассоциацию, которая является коллекцией, вы получаете к ним доступ через это поле коллекции.И когда это ассоциация ManyToMany, поэтому у вас нет таблицы соединений, у которой есть сопоставленная сущность, вам нужно объединиться с полем сбора.И поставщик JPA автоматически переводит это для объединения с таблицей соединения.

Как если вы запрашиваете фильмы определенных жанров , это пойдет

 SELECT m FROM Movie m JOIN m.genres mg WHERE mg.id = :genreId 

Производительность1019 * Как вы заметили в сгенерированном SQL, существует много уровней объединения для выборки и фильтрации данных.Это приводит к узким местам производительности.Чтобы преодолеть это, у вас может быть сущность для таблицы movie_genre. Этот сценарий справедливо обсуждается здесь Лучший способ сопоставить связь «многие ко многим» с дополнительнымистолбцы при использовании JPA и Hibernate

0 голосов
/ 20 сентября 2018

Вам не нужен модель movie_genre для того, чтобы написать оператор 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;
}
}

Рабочий пример 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());


}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...