SpringBoot

Hibernate Lazy Loading: 프록시 객체의 동작 원리와 실제 객체 조회 흐름

Terror123 2025. 3. 20. 10:32

개요

  • 실제 비즈니스 로직에서의 LazyLoading의 실행흐름에 대해 파악 할 수 있습니다.

사전준비

  • Caregiver <-> User <-> Patient
  • 현재 3개는 서로 OneToOne관계를 가지고있으며, User Entity에서 옵션을 관리하고있다.

비즈니스 로직

    @Transactional
    public CareMatchingDetailResponse generate(final AuthUser authUser, final CareMatchingRequestDto dto) {
        User user = userQuery.readById(authUser.getUserId());
        Patient patient = null;
        Caregiver caregiver = null;
        if (dto.getUserType().equals(UserType.CAREGIVER)) {
            patient = user.getPatient();
            caregiver = caregiverQuery.readById(dto.getTargetId());
        }
        if (dto.getUserType().equals(UserType.PATIENT)) {
            patient = patientQuery.readById(dto.getTargetId());
            caregiver = user.getCaregiver();
        }
        CareValidate.validateSelfCareMatching(patient, caregiver);
        Boolean isCareMatching = careMatchingQuery.existsByActiveUserInCareMatching(patient, caregiver);
        CareValidate.validateCareMatching(isCareMatching);
        CareMatching careMatching = CareMatching.of(patient, caregiver, dto);
        CareMatching saveCareMatching = careMatchingCommand.create(careMatching);
        return CareMatchingDetailResponse.of(saveCareMatching);
    }

예상

  • dto.getUserType().equals(UserType.CAREGIVER) 의 분기문을 탄다고 가정해보자
  • User를 DB에서 찾아온다 (이때 Caregiver,Patient 둘다 프록시 객체로 감싸져있다)
  • User안에 있는 Patient를 호출할때, 프록시 객체가 아닌 실제 객체로 바뀐다

실제

  • User를 DB에서 찾아온다 (이때 Caregiver,Patient 둘다 프록시 객체로 감싸져있다)

    • 실제로도 두객체모두 프록시 객체로 잘 감싸져있는 모습이다.

  • User안에 있는 Patient를 호출할때, 프록시 객체가 아닌 실제 객체로 바뀐다

    • 실제 객체가 필요하여 호출하였음에도, 아직 프록시 객체로 감싸져 있는 모습이다.

왜 안되지?

  • LazyLoading에 대한 개념을 다시한번 정리하고 갈 필요가있다.
  • 우리가 Lazy를 걸어둔 Entity에 대해서 실제 그 객체를 사용하기 이전까지는 쿼리가 날라가지 않는다.
  • 그리고 쿼리를 날라갔다고해서 인텔리제이상에서 보이는 객체는 여전히 Null로 찍힌다
  • 그럼 우리는 필드 접근 -> 프록시 객체 -> 실제 객체를 타고 가는 흐름은 유지가 되는것이지
  • 필드 접근 -> 실제 객체를 타고가는 흐름으로 변경되는 것이 아니다

User Entity를 조회하는 쿼리를 봐보자

Hibernate: 
    select
        u1_0.id,
        u1_0.auth_provider,
        u1_0.caregiver_id,
        u1_0.created_at,
        u1_0.email,
        u1_0.nickname,
        u1_0.password,
        u1_0.patient_id,
        u1_0.phone_number,
        u1_0.resident_registration_number,
        u1_0.updated_at,
        u1_0.user_role,
        u1_0.user_wallet_id 
    from
        user u1_0 
    where
        u1_0.id=?
  • 매핑되어있는 Caregiver,Patient는 가져오지않고 User객체 단 하나만 가져오는 모습이다

User안의 Patient를 조회할경우?

Hibernate: 
    select
        p1_0.id,
        p1_0.created_at,
        p1_0.diagnosis,
        p1_0.is_visible,
        p1_0.location,
        p1_0.name,
        p1_0.updated_at,
        u1_0.id,
        u1_0.auth_provider,
        u1_0.caregiver_id,
        u1_0.created_at,
        u1_0.email,
        u1_0.nickname,
        u1_0.password,
        u1_0.patient_id,
        u1_0.phone_number,
        u1_0.resident_registration_number,
        u1_0.updated_at,
        u1_0.user_role,
        u1_0.user_wallet_id 
    from
        patient p1_0 
    left join
        user u1_0 
            on u1_0.id=p1_0.user_id 
    where
        p1_0.id=?
  • 그때 Lazy가 실행되면서, Patient에 대한 객체를 가져오는 모습을 확인 할 수 있다.

오늘 나는 무엇을 알았는가?

  • 프록시 객체에 접근할때 쿼리가 수행된다
  • 프록시 객체에대해 쿼리를 수행한후 실제 객체를 가져오더라도, 프록시 객체는 벗겨지는것이 아닌 프록시 객체를 통해 실제 객체를 가져오는 흐름을 유지한다.