0

I have a problem with hibernate, namely with lazy data fetching. I have two entities and one of them clinic is having relation @OneToMany. I setup FetchType to LAZY, but still, when I am making request to get all clinic, I still have an array in the clinic entity. The questions is - why I am still getting full @OneToMany array? Shouldn't it be null?

Here I provided my entity classes

@Getter
@Setter
@Entity(name = "clinic")
public class Clinic {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String name;
  @OneToMany(fetch = FetchType.LAZY, mappedBy="clinic")
  private List<Doctor> doctors;
}

And doctor entity

@Getter
@Setter
@Entity(name = "doctor")
public class Doctor {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
  private String firstname;
  private String lastname;
  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnore
  private Clinic clinic;
}

And my controller, which is JpaRepository interface

@RestController
@RequestMapping("clinics")
public class ClinicController {

  private final ClinicRepository clinicRepository;

  public ClinicController(ClinicRepository clinicRepository) {this.clinicRepository = clinicRepository;}

  @GetMapping()
  public List<Clinic> getAllClinics() {
    return clinicRepository.findAll();
  }
}

I also logged SQL provided by Hibernate and it looks like this

Hibernate: select clinic0_.id as id1_0_, clinic0_.name as name2_0_ from clinic clinic0_
Hibernate: select doctors0_.clinic_id as clinic_i4_1_0_, doctors0_.id as id1_1_0_, doctors0_.id as id1_1_1_, doctors0_.clinic_id as clinic_i4_1_1_, doctors0_.firstname as firstnam2_1_1_, doctors0_.lastname as lastname3_1_1_ from doctor doctors0_ where doctors0_.clinic_id=?
Hibernate: select doctors0_.clinic_id as clinic_i4_1_0_, doctors0_.id as id1_1_0_, doctors0_.id as id1_1_1_, doctors0_.clinic_id as clinic_i4_1_1_, doctors0_.firstname as firstnam2_1_1_, doctors0_.lastname as lastname3_1_1_ from doctor doctors0_ where doctors0_.clinic_id=?

As I understand, it supposed to send only one query (first select).

And of course, I also will provide an answer that I am getting right now

[
    {
        "id": 1,
        "name": "Clinic 1",
        "doctors": [
            {
                "id": 1,
                "firstname": "Bob",
                "lastname": "Fisher"
            },
            {
                "id": 2,
                "firstname": "John",
                "lastname": "Cena"
            }
        ]
    },
    {
        "id": 2,
        "name": "Clinic 2",
        "doctors": [
            {
                "id": 3,
                "firstname": "Some",
                "lastname": "guy"
            }
        ]
    }
]
valart
  • 61
  • 1
  • 3
  • it's because by default spring-boot uses open-in-view entity manager if you use JPA and have a web application. take a look here: https://stackoverflow.com/questions/34641670/restcontroller-methods-seem-to-be-transactional-by-default-why – Andrei Titov Aug 22 '22 at 18:46
  • @AndrewThomas still does not solve my problem. I can not use `spring.jpa.open-in-view=false` – valart Aug 22 '22 at 19:36
  • why can't you use it? if you catch Hibernate exception like "cannot instantiate lazy object - no Session", that's okay, because Jackson is not able to serialize an object with lazy proxy instead of a collection without a session now, so you might need to define a DTO class without collection of doctors for this particular case and return it instead of an entity class, or add `@JsonIgnore` in the entity class. – Andrei Titov Aug 22 '22 at 19:58

1 Answers1

0

The association is lazily loaded because Jackson (JSON serialization library) simply serializes this proxy collection. Lazy loading is possible because Spring enables the open-session-in-view by default. If you turn that config off, you will see a LazyInitializationException.

What you want though, is to tell Jackson not to serialize the collection, by annotating it with @JsonIgnore:

@JsonIgnore
@OneToMany(fetch = FetchType.LAZY, mappedBy="clinic")
private List<Doctor> doctors;
Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
  • In this case You are not serializing collection at all. By that I mean even if I will call `clinic.getDoctors().size()`, I still will not get any collection – valart Aug 24 '22 at 19:08
  • You asked "The questions is - why I am still getting full @OneToMany array? Shouldn't it be null?" and the answer is - no, Hibernate initializes a proxy that supports lazy loading and when you access it, lazy initialization will happen. If you have another question, create another SO question please. – Christian Beikov Aug 25 '22 at 07:41