1

I am joining three tables in a Spring Data JPA Specification, as such:

public static Specification<Contact> findByStructureCode(final String code) {
    return new Specification<Contact>() {
        @Override
        public Predicate toPredicate(Root<Contact> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            return criteriaBuilder.equal(root.join(Contact_.contactToStructures)
                    .join(ContactToStructure_.structureCodes)
                    .get(StructureCode_.code), code);
        }
    };
}

However, this is giving me a good old NullPointerException.

java.lang.NullPointerException: null
    at org.hibernate.jpa.criteria.path.AbstractFromImpl.constructJoin(AbstractFromImpl.java:345) ~[hibernate-entitymanager-4.3.8.Final.jar:4.3.8.Final]
    at org.hibernate.jpa.criteria.path.AbstractFromImpl.join(AbstractFromImpl.java:333) ~[hibernate-entitymanager-4.3.8.Final.jar:4.3.8.Final]
    at org.hibernate.jpa.criteria.path.AbstractFromImpl.join(AbstractFromImpl.java:324) ~[hibernate-entitymanager-4.3.8.Final.jar:4.3.8.Final]
    at my.package.ContactSpecification$3.toPredicate(ContactSpecification.java:83) ~[MyProject-1.0.0.jar:na]

Note that I have replaced package and jar information in that last line with fake information. The line in my code that the stack trace references is .join(ContactToStructure_.structureCodes).

I believe this is being caused by the fact that not all Contact rows have corresponding ContactToStructure rows in my database.

So, how can I do the first join to allow for Contact entities with an empty contactToStructures set, and just return contacts with non-empty contactToStructures sets?


Here are the relevant pieces of the entities and their metamodels.

Contact.java

@Entity
@Table(name = "CONTACT", schema = "myschema")
public class Contact {    
    @OneToMany(mappedBy = "contact", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
    private Set<ContactToStructure> contactToStructures = new HashSet<>(0);    
}

Contact_.java

@StaticMetamodel(Contact.class)
public class Contact_ {
    public static volatile SetAttribute<Contact, ContactToStructure> contactToStructures;
}

ContactToStructure.java

@Entity
@Table(name = "CONTACT_TO_STRUCTURE", schema = "myschema")
public class ContactToStructure {
    @OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL}, mappedBy="contactToStructure")
    private Collection<StructureCode> structureCodes = new ArrayList<>();
}

ContactToStructure_.java

@StaticMetamodel(ContactToStructure.class)
public class ContactToStructure_ {
    public static volatile SetAttribute<ContactToStructure, StructureCode> structureCodes;
}

StructureCode.java

@Entity
@Table(name = "STRUCTURE_CODE", schema = "myschema")
public class StructureCode {
    @Column(name = "CODE")
    private String code;
}

StructureCode_.java

@StaticMetamodel(StructureCode.class)
public class StructureCode_ {
    public static volatile SingularAttribute<StructureCode, String> code;
}
Andrew Mairose
  • 10,615
  • 12
  • 60
  • 102
  • It's not what you fear (in fact, the query you are building should yield the behavior you desire). The exception is being thrown long before any query is sent to the database. My belief is that you are running into this problem: http://stackoverflow.com/questions/3854687/jpa-hibernate-static-metamodel-attributes-not-populated-nullpointerexception – Pace Nov 24 '15 at 22:36
  • Nope, my metamodels are all in the same package as their corresponding entity, and are named exactly the same, other than the metamodel class name and file name containing an underscore at the end. – Andrew Mairose Nov 26 '15 at 03:32

1 Answers1

0

If still relevant try to make the following change ContactToStructure_.java:

public static volatile SetAttribute<Version, UseCase> useCases;

To:

public static volatile CollectionAttribute<Version, UseCase> useCases;
oak
  • 2,898
  • 2
  • 32
  • 65