JPA Criteria API filtering on subclasses' attribute

烈酒焚心 提交于 2020-03-23 08:18:04

问题


I have the following entity domain.

Parent class

@Entity
public final class SupportModuleInteraction implements Serializable {

    @Id
    private Long id;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "supportModuleInteraction")
    @OrderColumn
    private List<Event> events;

    // ... getters/setters/ other attributes ommitted
}

Child class hierachy

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "event_type")
public abstract class Event implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(fetch = FetchType.EAGER)
    private SupportModuleInteraction supportModuleInteraction;
}


@Entity
@DiscriminatorValue("SEARCH")
public final class SearchEvent extends Event {

    @Lob
    private String searchTerm;
}

I'm trying to retrieve any SupportModuleInteractions that contain a SearchEvent containing a keyword.

This is the Specification I'm using to try and retrieve those SupportModuleInteractions:

public static Specification<SupportModuleInteraction> interactionWithSearchEventContaingKeyword(String keyword) {
        return new Specification<SupportModuleInteraction>() {
            @Override
            public Predicate toPredicate(Root<SupportModuleInteraction> smi, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Join<SupportModuleInteraction, SearchEvent> events = smi.join("events");
                return criteriaBuilder.and(
                        criteriaBuilder.equal(events.type(), SearchEvent.class),
                        criteriaBuilder.like(events.get("searchTerm"), "%" + keyword + "%"));
            }
        };
    }

The Specification fails saying that Event contains no attribute called "searchTerm" which makes sense as that's present only on the SearchEvent subclass.

I've looked at this answer here JPA2 Criteria queries on entity hierarchy which doesn't solve my issue as the entity involved in the query lies outside the class hierarchy.

As a native SQL query, this is what I'm trying to acheive:

SELECT *
FROM event E JOIN support_module_interaction smi ON E.support_module_interaction_id = smi.id
WHERE event_type = 'SEARCH'
  AND search_term LIKE 'searchString'

UPDATE 31/08/2019

I've looked at JPA Criteria API: query property of subclass and revised the Specification to use criteriaBuilder.treat(...) with no luck:

 public static Specification<SupportModuleInteraction> interactionWithSearchEventContaingKeyword(String keyword) {
        return new Specification<SupportModuleInteraction>() {
            @Override
            public Predicate toPredicate(Root<SupportModuleInteraction> smi, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                Join<SupportModuleInteraction, SearchEvent> events = smi.join("events");
                return criteriaBuilder.and(
                        criteriaBuilder.equal(events.type(), SearchEvent.class),
                        criteriaBuilder.like(
                                criteriaBuilder.treat(events.get("searchTerm"), SearchEvent.class).get("searchTerm"),
                                "%" + keyword + "%"));
            }
        };
    }

回答1:


The various inheritance strategies in Hibernate are rather difficult to work with in the JPA Crtieria API.

Instead of using treat(), which is rather finicky (see HHH-9862 and related issues), generate a subquery() expression which filters for searchTerm and add that to your parent query.

Your overall implementation is unclear, so I created a basic example:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<SupportModuleInteraction> cq = cb.createQuery(SupportModuleInteraction.class);

Root<SupportModuleInteraction> supModIntRoot = cq.from(SupportModuleInteraction.class);
Join<SupportModuleInteraction, Event> supModIntEventJoin = supModIntRoot.join("events"); 

// Instantiate the Subquery and Root objects for your subclass entity
Subquery<SearchEvent> searchEventSubquery = cq.from(SearchEvent.class);
Root<SearchEvent> searchEventRoot = searchEventSubquery.from(SearchEvent.class);
// Generate the subquery and filter based on the given parameter
searchEventSubquery.select(searchEventRoot);
searchEventSubquery.where(cb.like(searchEventRoot.get("searchTerm", "%" + searchTerm + "%");

// add subquery to where clause in parent query
cq.select(supModIntRoot);
cq.where(supModIntEventJoin.in(searchEventSubquery), //other subqueries and filters...);


来源:https://stackoverflow.com/questions/57734118/jpa-criteria-api-filtering-on-subclasses-attribute

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!