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.

New
  • 675
  • 1
  • 11
  • 21
Alessandro C
  • 3,310
  • 9
  • 46
  • 82
  • 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`. – Arnaud Denoyelle Mar 29 '16 at 13:09
  • I converted into: @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) private Contract contract; but I still have the problem. – Alessandro C Mar 29 '16 at 13:15
  • also try to replace the predicate with this : `predicates.add(cb.equal(root.join("contract").get("contractType"), searchParam.getContract()))` – Arnaud Denoyelle Mar 29 '16 at 13:24
  • And remove `fetch = FetchType.LAZY` on the relationship. – Arnaud Denoyelle Mar 29 '16 at 13:25
  • if i remove fetch = FetchType.LAZY it gives me the error: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags – Alessandro C Mar 29 '16 at 14:06

1 Answers1

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

Community
  • 1
  • 1
Walter G.
  • 135
  • 4
  • 12