JPA Criteria API过滤子实体

时间:2018-01-24 21:32:18

标签: java hibernate jpa spring-data-jpa jpa-criteria

代码示例:

    @Entity
    public class Event {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @OneToMany(mappedBy="event")
    private List<Actions> actions;

    }


    @Entity
    public class Action {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private Date date;
    @ManyToOne
    @JoinColumn(name = "event_id")
    private Event event;

    }

    public class EventSpecification {

    public static Specification<Event> findByCriteria(EventSearchCriteria criteria) {

        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            criteria.getDate().ifPresent(date -> {
                Join<Event, Action> join = root.join(Event_.actions);
                predicates.add(criteriaBuilder.equal(join.get(Action_.date), criteria.getDate().get()));
            });

            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
    }
    }
...

eventRepository.findAll(EventSpecification.findByCriteria(searchCriteria))

在我的数据库中,我有下一个:

Event Table
id, name
1, 'event_1'
2, 'event_2'

Action Table
id, name, date, event_id
1, 'action_event_1_1', '01-01-2018', 1
2, 'action_event_1_2', '01-01-2018', 1
3, 'action_event_1_3', '01-02-2018', 1

4, 'action_event_2_1', '01-03-2018', 2

将我的规范代码与data =&#39; 01-01-2018&#39;一起使用,结果我获得了相同Event对象的列表,列表的大小是已连接对象的数量,克劳斯Action.date = criteria.date:

[{id=1, name=event_1, actions=[all 3 actions]},
{id=1, name=event_1, actions=[all 3 actions]}]

我需要得到下一个结果:

{id=1, name=event_1, actions=[only two actions with date '01-01-2018']}

我尝试添加

criteriaQuery.groupBy(root.get(Event.id));

它修复了结果列表大小 -

{id=1, name=event_1, actions=[all 3 actions]}

但我仍然得到了事件1的所有3个动作。

问题是:

是否可以只获取包含具有所需日期的操作的事件,以便每个事件仅包含一个包含所请求日期的操作的列表?

2 个答案:

答案 0 :(得分:1)

我找到了下一个解决方案:

public static Specification<Event> findByCriteria(EventSearchCriteriacriteria) {

        return (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();

            criteria.getDate().ifPresent(date -> {
                criteriaQuery.distinct(true);
                Join<Event, Action> join = (Join<Event, Action>)root.fetch(Event_.actions);
                predicates.add(criteriaBuilder.equal(join.get(Action_.date), criteria.getDate().get()));
            });

            return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
        };
    }

可能Join<Event, Action> join = (Join<Event, Action>)root.fetch(Event_.actions);可能不是最佳解决方案,因为我的IDE会警告我关于转换,但这是我找到的唯一可行解决方案。

顺便说一下,如果不使用Criteria API,它的工作原理很完美:

@EntityGraph(value = "Event.actions", type = EntityGraph.EntityGraphType.LOAD)
@Query("select distinct e from Event e join e.actions a where a.date = (:date)")
List<Event> findAllWithActionsByDate(@Param("date") Date date);

但在我的案例中,避免使用Criteria API是不合适的。

答案 1 :(得分:0)

试试这个JPQL查询:

select e from Events e join Actions a with a.date=:date
group by e