【问题标题】:Complex Sort with Spring Data JPA使用 Spring Data JPA 进行复杂排序
【发布时间】:2016-01-11 13:23:39
【问题描述】:

我正在使用 Spring Data Specifications and Sort APIs(我的存储库是 JpaSpecificationExecutor),我的数据模型看起来像这样(精简了一点)

@Entity
public class Message extends AbstractVersionedEntity {
    @OneToMany
    private Set<MessageOwner> messageOwners = new HashSet<>();
}

@Entity
public class MessageOwner extends AbstractVersionedEntity  {        
    @ManyToOne
    private Mailbox owner;

    @Enumerated(EnumType.STRING)
    private MessageOwnerType type;
}

@Entity
public class Mailbox extends AbstractVersionedEntity {        
    @Column(unique=true)
    private String ldapId;
}

我需要按收件人对消息进行排序 - 即按 MessageOwnerType.TO 类型的 messageOwners 邮箱的 ldapId。

message->messageOwners(type=TO)->owner->ldapId

此外,应为每条消息对收件人进行排序 - 如果有多个 messageOwner 类型为 MessageOwnerType.TO,则应选择按字母顺序排列的第一个作为消息的排序值,可以这么说。

在纯 SQL 中,我可能会使用某种子查询来执行此操作。是否可以使用 Spring Data Specifications & Sort 对象进行类似的操作?除非必须,否则我宁愿不必重写 QueryDSL/raw Criteria 中的所有内容。

【问题讨论】:

  • 您是否总是希望数据按该顺序排序?还是只有一次?如果是前者,请在实体上添加@OrderBy。

标签: java spring sorting spring-data spring-data-jpa


【解决方案1】:

好的,所以我想出了如何使用规范来做到这一点,但这并不漂亮。我不能使用 Sort 对象,而是使用 JPA CriteriaBuilder 将 orderBy 子句放在规范中。

这实际上导致了 SimpleJpaRepository 的问题,它在所有可分页的 findAll 查询之前执行 select count()。我不得不禁用它(在this Stackoverflow answer 的帮助下。

所以我的规范看起来像这样:(讨厌,我知道。如果有人有更好的建议,我很想听听)

@Override public Predicate toPredicate(Root<Message> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    // Note - this would be simpler if JPA allowed subqueries in joins. As it doesn't,
    // we have to put the subquery in a where clause, join both sides with the mailbox
    // and correlate the results

    // join the main query on the mailbox (for the ORDER BY clause)
    Join<Message, MessageOwner> messageOwners = root.join("messageOwners", JoinType.LEFT);
    Join<Message, Mailbox> mailbox = messageOwners.join("owner");

    // create a subquery and correlate the messages with the main query
    Subquery<String> subQuery = query.subquery(String.class);
    Root<Message> sqMessage = subQuery.from(Message.class);
    Root<Message> correlateMessage = subQuery.correlate(root);

    // join the subquery on the mailbox
    Join<Object, Object> sqMessageOwners = sqMessage.join("messageOwners", JoinType.LEFT);
    Join<Message, Mailbox> sqMailbox = sqMessageOwners.join("owner");

    // get the lowest ldapId alphabetically 
    Expression<String> minLdapId = cb.least(sqMailbox.<String>get("ldapId"));

    // the actual subquery
    // select the lowest ldapId for the current message (see the group-by clause)
    // where the recipient type is TO and the message is the same as the main query
    subQuery.select(minLdapId)  
            .where(
                    cb.and(     
                            cb.equal(sqMessageOwners.get("type"), MessageOwnerType.TO),
                            cb.equal(sqMessage, correlateMessage)))
            .groupBy(sqMessage.get("id")); 

    // the subquery gives us the lowest TO recipient for each mail
    // sort on these values. Note: his must be done here rather than in the Sort property
    // of the Pageable in order to maintain the connection between the mailbox in the order by clause
    // and the one in the subquery
    Path<String> recipientOrderClause = mailbox.get("ldapId");
    Order recipientOrder = sortDirection == Sort.Direction.ASC ? cb.asc(recipientOrderClause) : cb.desc(recipientOrderClause);

    // secondary sorting descending by sendTime
    Order sendTimeOrder = cb.desc(root.get("sendTime"));

    // adding order by queries here, rather than via the Sort object
    // is something of a violation of standard Spring Data practice. (see above for the reason it is done)
    // this precludes us from making some grouped calls such as select count(*)
    query.orderBy(recipientOrder, sendTimeOrder);

    // attach the subquery onto the query and return.
    return cb.equal(mailbox.get("ldapId"), subQuery);
}

【讨论】:

    猜你喜欢
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 2013-07-25
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 2022-08-10
    • 1970-01-01
    相关资源
    最近更新 更多