【问题标题】:JPA Criteria API filter subentitiesJPA Criteria API 过滤子实体
【发布时间】:2018-07-04 01:22:02
【问题描述】:

代码示例:

    @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='01-01-2018' 一起使用,结果我得到了一个相同事件对象的列表,并且列表的大小是连接对象的计数,满足子句 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 个动作。

问题是:

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

【问题讨论】:

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


    【解决方案1】:

    试试这个 JPQL 查询:

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

    【讨论】:

    • 我可以,但是可以使用规范吗?我正在尝试实现一个搜索过滤器,因此以这种方式执行它很重要。
    【解决方案2】:

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

    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&lt;Event, Action&gt; join = (Join&lt;Event, Action&gt;)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 是不合适的。

    【讨论】:

    • 为什么不直接使用Join&lt;Event, Action&gt; join = root.join(Event_.actions)?无需投射
    • 字段动作有延迟初始化。只取让我在“单个”数据库查询中得到我需要的东西——“......每个事件只包含一个带有请求日期的操作的列表”。使用 join 代替,我只得到事件对象,但没有 Action.date=date 的动作。
    • 嗯,好的。您是否尝试过使用join 而不是fetch 并应用Oracle 的Java EE Tutorial 中所示的EntityGraph 提示?
    • 不,我没有,但我认为应该可以。我没有这样做,因为在我的情况下它比获取方法更难,我在这里只展示我的过滤器的一小部分......谢谢。
    猜你喜欢
    • 2014-11-20
    • 1970-01-01
    • 1970-01-01
    • 2018-10-16
    • 2012-03-15
    • 1970-01-01
    • 2015-08-14
    • 2014-09-01
    • 1970-01-01
    相关资源
    最近更新 更多