Home > Net >  JPA / Criteria API - How to filter a OneToMany field?
JPA / Criteria API - How to filter a OneToMany field?

Time:09-15

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

CodePudding user response:

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"));

CodePudding user response:

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 the source type (As I understand, it is "Message")
  • @param 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).

  • Related