0

I have an implementation with Spring Data REST for a Person class and a Contract class.

This is my Person.class:

@Entity
@Table
public class Person {

    @Column
    private String name;

    @Column
    private String surname;

    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    private List<Contract> contracts;

}

This is my Contract.class:

@Entity
@Table
public class Contract {

    @Column
    private String contractType;

    @Column
    private String contractDesc;

}

These are my tables definitions and data (script auto-generated in an embedded HSQL DataBase):

CREATE MEMORY TABLE PUBLIC.PERSON(ID, NAME, SURNAME, CONTRACT_ID);
CREATE MEMORY TABLE PUBLIC.CONTRACT(ID, CONTRACTTYPE, CONTRACTDESC);
ALTER TABLE PUBLIC.PERSON ADD CONSTRAINT FK_ABCDEFGHIJKLMNOPQRSTUVWXY FOREIGN KEY(CONTRACT_ID) REFERENCES PUBLIC.CONTRACT(ID);
INSERT INTO PERSON VALUES('9876543210abcdefghijklmnopqrstuv', 'Jack', 'Reds','1234567890abcdefghijklmnopqrstuv');
INSERT INTO CONTRACT VALUES('1234567890abcdefghijklmnopqrstuv', 'OTHER', 'Other');

This is the response obtained from Spring Data REST, at my url: localhost/my-app/api/persons/

{
  "_embedded" : {
    "persons" : [ {
      "name" : "Jack",
      "surname" : "Reds",
      "contract" : {
        "contractType" : "OTHER",
        "contractDesc" : "Other",
    ...

But if I execute this portion of code:

List<Person> persons = personRepository.findAll(new PersonSpec("TEST"));

in debug the variable "persons" is valorized with this data:

persons= ArrayList<E> (id=233)
    elementData=Object[7] (id=235)
        [0]= Person (id=236)
            name= "Jack" (id=176)
            surname= "Reds" (id=180)
            contract= Contract_$$_jvst951_d (id=240)
                contractType= null
                contractDesc= null
        [1]= Person (id=237)
        [2]= Person (id=238)
    modCount=1
    size = 3

"contractType" and "contractDesc" are setted to NULL. Furthermore, my PersonSpec seems to don't make any filtering with these fields:

public class PersonSpec implements Specification<Person> {

    @Override
    public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

            if (StringUtils.isNotBlank(searchParam.getContract())) {
                predicates.add(cb.and(cb.and(cb.isNotNull(root.get("contract")),
                                             cb.isNotNull(root.get("contract").get("contractType"))),
                                       cb.equal(root.get("contract").get("contractType"), searchParam.getContract())));
            }

What's wrong? I don't understand why there is no link between my Person and my Contract.

Thanks.

5
  • There is a discrepancy between your datamodel and your mapping. According to the datamodel, a Person has one Contract (because table PERSON has a column CONTRACT_ID) but according to the mapping, a Person has a List<Contract>. Commented Mar 29, 2016 at 13:09
  • I converted into: @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) private Contract contract; but I still have the problem. Commented Mar 29, 2016 at 13:15
  • also try to replace the predicate with this : predicates.add(cb.equal(root.join("contract").get("contractType"), searchParam.getContract())) Commented Mar 29, 2016 at 13:24
  • And remove fetch = FetchType.LAZY on the relationship. Commented Mar 29, 2016 at 13:25
  • if i remove fetch = FetchType.LAZY it gives me the error: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags Commented Mar 29, 2016 at 14:06

1 Answer 1

1

This usually happens because of the LAZY loading - when you do the @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) it doesn't take care of bringing the Contract data, hence why it displays as null.

You can try by changing the fetch = FetchType.LAZY to fetch = FetchType.EAGER or instantiate the object yourself.

Here's some extra info on LAZY vs EAGER:

UPDATE
I know it has been a while, but another way that you can fetch a lazy collection it's by doing on your service something like:

persons.contracts.size();

Just make sure that your method includes the @Transactional annotation

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.