Search code examples
hibernatejpamany-to-manyquerydslpredicate

QueryDSL and ManyToMany relationships


In a project I have the following entities : templates which have template parts which are connected to keywords. Both of these relationships are @ManyToMany. Some code :

@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "TEMPLATE")
public class TemplateEntity extends SystemFlagEntity {

    ...

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "TEMPLATEPART_CATALOGUE",
            joinColumns = @JoinColumn(name = "C_FK_TEMPLATE", referencedColumnName = "C_I_IDF"),
            inverseJoinColumns = @JoinColumn(name = "C_FK_TEMPLATEPART", referencedColumnName = "C_I_IDF"))
    Set<TemplatePartEntity> templatePartsCatalogue = new HashSet<>();

}

@ToString
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "TEMPLATEPART")
public class TemplatePartEntity extends SystemFlagEntity {

    ...

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
            name = "TEMPLATEPART_KEYWORD",
            joinColumns = @JoinColumn(name = "C_FK_TEMPLATEPART"),
            inverseJoinColumns = @JoinColumn(name = "C_FK_KEYWORD"))
    Set<KeywordEntity> keywords = new HashSet<>();

}

@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "KEYWORD")
public class KeywordEntity extends LabeledEntity {

    ...

}

Then I try to find the keywords related to some template with a given ID. I do this with QueryDSL using this predicate :

public Iterable<KeywordEntity> findCatalogueKeywordsForTemplate(Long id) {
    return findAll(
        QKeywordEntity.keywordEntity.in(QTemplateEntity.templateEntity.templatePartsCatalogue.any().keywords)
            .and(QTemplateEntity.templateEntity.id.eq(id))
    );
}

For some reason the predicate doesn't process well at all. I get this error when trying to call the method :

org.hibernate.hql.internal.ast.QuerySyntaxException: templateEntity.templatePartsCatalogue is not mapped [select keywordEntity\nfrom mypackage.core.domain.model.KeywordEntity keywordEntity\nwhere exists (select 1\nfrom templateEntity.templatePartsCatalogue as templateEntity_templatePartsCatalogue_0\nwhere keywordEntity member of templateEntity_templatePartsCatalogue_0.keywords) and templateEntity.id = ?1]; nested exception is java.lang.IllegalArgumentException: org.hibernate.hql.internal.ast.QuerySyntaxException: templateEntity.templatePartsCatalogue is not mapped [select keywordEntity\nfrom mypackage.core.domain.model.KeywordEntity keywordEntity\nwhere exists (select 1\nfrom templateEntity.templatePartsCatalogue as templateEntity_templatePartsCatalogue_0\nwhere keywordEntity member of templateEntity_templatePartsCatalogue_0.keywords) and templateEntity.id = ?1]

It looks like it doesn't process the @ManyToMany associations well. Any idea on how to fix this problem? I know I can use JPAQuery explicitely, but I'd like to use a Predicate instead.


Solution

  • I figured out that I misunderstood the way the Predicate works after seeing this answer by QueryDSL's creator. This query works without needing to explicitely define many-to-many association tables :

    public Iterable<KeywordEntity> findCatalogueKeywordsForTemplate(Long id) {
        return new JPAQuery<>(entityManager)
                .select(QKeywordEntity.keywordEntity)
                .from(QTemplateEntity.templateEntity)
                .innerJoin(QTemplateEntity.templateEntity.templatePartsCatalogue, QTemplatePartEntity.templatePartEntity)
                .innerJoin(QTemplatePartEntity.templatePartEntity.keywords, QKeywordEntity.keywordEntity)
                .where(interceptPredicate(QTemplateEntity.templateEntity.id.eq(id)))
                .distinct()
                .fetch();
    }