spring-data-jpa JPA / Criteria API - How to filter a OneToMany field?

xnifntxz  于 2022-11-10  发布在  Spring
关注(0)|答案(2)|浏览(160)

Data domain (kind of email model) :

- Message(id,subject,messageAttachment) OneToMany MessageAttachment(messageId,attachmentSHA1,message)
- Message(id,subject,messageAddress) OneToMany MessageAddress(messageId,email,type,message)

(@ManyToOne and OneToMany are bi-directionals)
My goal is to do :

SELECT
  MessageAttachment.attachmentSHA1,
  Message.subject,
  MessageAddress.email
FROM MessageAttachment 
  JOIN FETCH Message {ON clause should be generated based on the domain}
  JOIN FETCH MessageAttachment {ON clause should be generated based on the domain}
WHERE type='ReplyTo'

I want of course to avoid the N+1 problem (so I want just one select).
And I need the Criteria API to add dynamically specifications to the query.
So, something like that :

void go() {
    CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
    CriteriaQuery<MessageAttachment> query = criteriaBuilder.createQuery(MessageAttachment.class);
    Root<MessageAttachment> root = query.from(MessageAttachment.class);
    Fetch<Message, MessageAttachment> fetch = root.fetch("message").fetch("messageAddress");
    //query.select(root).where(criteriaBuilder.equal(root.get("message").get("messageAddress").get("type"), "ReplyTo"));
    List<MessageAttachment> l = em.createQuery(query).setMaxResults(2).getResultList();
    System.err.println(l);
}

If I remove the comment, I get this Exception : Caused by: java.lang.IllegalStateException: Illegal attempt to dereference path source [null.message.messageAttachment] of basic type

8fq7wneg

8fq7wneg1#

Try using this instead:

Join<Message, MessageAttachment> messageAddress = (Join<Message, MessageAttachment>) root.fetch("message").fetch("messageAddress");
query.select(root).where(criteriaBuilder.equal(messageAddress.get("type"), "ReplyTo"));
ff29svar

ff29svar2#

Problem solved, thank you !
Remark, casting to (Join<Message, MessageAttachment>) didn't compiled, I needed to replaced it by (Join).
I still need have an unchecked cast warning, but I'm not sure it ispossible to avoid it.
Also, the X Y Z templates are a bit confusing.
Our code is :
root.fetch("message").fetch("messageAddress");
Documentation says :


* @param <Z>  the source type (As I understand, it is "Message")

 * @param <X>  the target type (As I understand, it is "MessageAddress")
 * public interface FetchParent<Z, X>

Method signature is :
<X, Y> Fetch<X, Y> fetch(String attributeName);
So X becomes the source (Z) and Y the target(Z) ...
So shouldn't our code be like this ?
Join<Message, MessageAddress> =(Join)root.fetch("message").fetch("messageAddress");
(Anyway, even Join<Object,Object> works as well).

相关问题