ManyToOne - always EAGER loading

I have spent quite a bit of time reading this great post about OneToMany and ManyToOne relationships.

As a result of that posted I have used the final method described to setup my relationshps - that is to simple annotation the @ManyToOne side and write a JPQL query to retrieve the parent and all the children.

The problem is despite defining fetch = FetchType.LAZY it is still fetching EAGER.

My specific objects are:

@Entity
@Data
public class HealthCeck {
    @Id
    @GeneratedValue(strategy - IDENTITY)
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Patient patient;
    // getters and setters

and on the OneTo side:

@Entity
@Data
public class Patient {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     private Long id;
     private String name;
     // other fields
     // getters and setters

When I run my springboot application the table is create similar to

PATIENT
id, firstname, surname, age etc

HEALTH_CHECK
id, otherfields, otherfields,…patient_id

and my JPQL if I need to get a Health_Check with Patient

@Query("select hc from HealthCheck where hc.patient.id = :patientId")
 Collection<HealthCheck> findHealthCheckByPatientId(@Param("patientId") Long id);

Now this all works but one thing I do not understand. If I retrieve a HealthCheck (where I have explicitly speicified LAZY loading of patient) instead of just getting the foreign key of patient I get the whole patient object.

Why?

In my application I will build DTOs, but for now I need to be able to retrieve a HealthCheck by Id with only the id number of the patient rather than the whole object.

PS: These objects are being deserialised in Jackson with a REST endpoint and thats where I am seeing the whole patient Object on a HealthCheck.

It was suggested to “reassure” hibernate that for every HealthCheck there is a patient, then hibernate doesn’t feel the need to EAGER load (and override my LAZY).

They suggested way to do this was:

    @ManyToOne(fetch = FetchType.LAZY, optional=false)
    private Patient patient;

Note the optional=false

I have however tried this and my REST controller still returns both the HealthCheck AND the Patient - why?

Are you seeing a second query or a join to patient in your main SQL query for fetching the patient data? Chances are, you are seeing the Patient association populated because the object is in the first or second level cache.
If you see a secondary query, try to determine when this query is issued exactly. It might be that your conversion to a DTO is triggering lazy loading.

I was basing my conclusion that its not LAZY loading on the basis that my REST controller was showing the child and the parent.

But other have suggested that when serialised all the entities are loaded - so looking at the output of the REST controller is not a good way to check if something was LAZY loaded.

(perhaps you know a better place in breakpoint/debugger to check this?)

Yeah, it’s quite easy. Just set a breakpoint right after loading the data i.e. after the call to findHealthCheckByPatientId

Also, since you are using Jackson probably, see this post: java - Serialization of @ManyToOne object with customized child object in JPA/Jackson - Stack Overflow

Thanks. Maybe getting slightly OT here, but something is becoming clear to me.

Lets say I have :

Patient (OneToMany) HealthCheck
HealthCheck (OneToMany) Patient Weight

But this does not mean that I should be attempting setup the relationships on the entity so that a request to the database for a HealthCheck brings back the parent Patient and the related children objects weight.

While the relationships should be defined, and there should be a way to get the parent with the children and visa versa - the object sent to the REST controller should normally be a DTO constructed in a service layer, with only the necessary fields sent for the particular view.

The method name to construct this DTO might be dtoGetHealthCheckwithPatientandWeights() etc?

the object sent to the REST controller should normally be a DTO constructed in a service layer, with only the necessary fields sent for the particular view.

Sounds like a better idea to me. You might find Blaze-Persistence Entity-Views interesting for this use case which I am the author of.

One more ORM question.
If I have a optional OneToOne, I assume i can’t setup the way described here with shared id, fk_id since its optional?

So just setup using a id_fk on the child?

You can share the FK column with the PK, but then you can’t use a FK constraint. To do that, you will also have to annotate the association with @NotFound(action = IGNORE), but I would advise against that if possible. Another alternative is to map this through a secondary table, but either way, Hibernate will have to always join the target table to know if the FK column points to an actual row.