Hibernate:多对多包含使用 Criteria 的特定 ID

Hibernate: Many to Many contains specific IDs using Criteria

我正在开发一个 JPA 应用程序,我遇到了这些实体之间多对多关联的问题:

电影

class Movie {
    @Id 
    String id;

    ...

    @ManyToMany
    @JoinTable(
        name = "movie_category",
        joinColumn = @JoinColumn(name = "movie_id", referencedColumnName = "id")
    )
    Set<Category> categories = new HashSet<>();
}

类别

class Category {

    @Id
    String id;
    ...
}

我只想 select 具有所有类别的电影(例如 List<Category>)。在 Criteria API 中,我可以这样做:

List<Category> requiredCategories = ... //from request


CriteriaBuilder b = em.getCriteriaBuilder();
CriteriaQuery<Movie> cq = b.createQuery(Movie.class);
Root<Movie> r = cq.from(Movie.class);

Expression<Set<Category>> categories = r.get(Movie_.categories);

Predicate predicate = b.and();
for(Category c : requiredCategories) {
    predicate = b.and(predicate, b.isMember(c, categories));
}

工作正常,但问题是我必须先从 Category table 获取 Category 对象,然后在 isMember 函数中使用它。我想以某种方式避免加入 Category table.

例如:我想要所有具有类别 COMEDYFANTASY 的电影。 (考虑这些关键字是 Category.id),所以我需要通过 id 从 Category table 两个实体中提取,然后在 isMember 函数中使用它们。我想避免这种情况,因为信息 category_id 已经存储在关联 table movie_category 中。 简而言之,我想要 isMember 之类的东西,但提供 Category.id 而不是实体。

这样的事情可能吗?

据我所知,使用 JPA 内置工具无法进行此类查询,因为无法直接访问连接 table。但这可以通过 FluentJPA:

public List<Movie> getMoviesByCategories(List<String> categoryIds) {
    int matchTotal = categoryIds.size();

    FluentQuery query = FluentJPA.SQL((Movie movie,
                                       JoinTable<Movie, Category> movieCategory) -> {

        discardSQL(movieCategory.join(movie, Movie::getCategories));

        List<String> movieIds = subQuery(() -> {
            String movieId = movieCategory.getJoined().getId();
            String catId = movieCategory.getInverseJoined().getId();

            SELECT(movieId);
            FROM(movieCategory);
            WHERE(categoryIds.contains(catId));
            GROUP(BY(movieId));
            HAVING(COUNT(movieId) == matchTotal); // COUNT(DISTINCT(movieId));
        });

        SELECT(movie);
        FROM(movie);
        WHERE(movieIds.contains(movie.getId()));

    });

    return query.createQuery(em, Movie.class).getResultList();
}

产生以下 SQL:

SELECT t0.* 
FROM t0 
WHERE (t0.id IN (SELECT t1.movie_id 
FROM movie_category t1 
WHERE (t1.CATEGORIES_id IN ?1 ) 
GROUP BY  t1.movie_id  
HAVING (COUNT(t1.movie_id) = ?2) ) )

子查询需要产生 and 语义。参见 this answer。 解释了 FluentJPA 中的多对多语义 here