인프런 커뮤니티 질문&답변

감바스님의 프로필 이미지
감바스

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

즉시 로딩과 지연 로딩

em.flush(), em.clear() 후 프록시 객체 조회에 대해 질문 있습니다.

작성

·

65

·

수정됨

0

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.Persistence;

public class Test {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();
        try {
            Team team = new Team();
            team.setName("team1");
            em.persist(team);

            Member member = new Member();
            member.setName("hi");
            member.setTeam(team);
            em.persist(member);

            em.flush();
            em.clear();

            Member findMember = em.find(Member.class, member.getId());
            System.out.println(findMember.getTeam().getClass());
            System.out.println(findMember.getTeam().getName());
            System.out.println("=========");

            Team team2 = new Team();
            team2.setName("team2");
            em.persist(team2);
            findMember.setTeam(team2);

            em.flush();
            em.clear();

            Member findMember2 = em.find(Member.class, member.getId());

            System.out.println(findMember2.getTeam().getClass());
            System.out.println(findMember2.getTeam().getName());

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        emf.close();
    }
}

실행결과

Hibernate: 
    /* insert for
        jpa_basic.ex1_hello_jpa.entity.Team */insert 
    into
        Team (name, TEAM_ID) 
    values
        (?, default)
Hibernate: 
    /* insert for
        jpa_basic.ex1_hello_jpa.entity.Member */insert 
    into
        Member (city, name, number, roleType, street, TEAM_ID, zipcode, MEMBER_ID) 
    values
        (?, ?, ?, ?, ?, ?, ?, default)
Hibernate: 
    select
        m1_0.MEMBER_ID,
        m1_0.city,
        m1_0.name,
        m1_0.number,
        m1_0.roleType,
        m1_0.street,
        m1_0.TEAM_ID,
        m1_0.zipcode 
    from
        Member m1_0 
    where
        m1_0.MEMBER_ID=?
class jpa_basic.ex1_hello_jpa.entity.Team$HibernateProxy$q7Fz4jvQ
Hibernate: 
    select
        t1_0.TEAM_ID,
        t1_0.name 
    from
        Team t1_0 
    where
        t1_0.TEAM_ID=?
team1
=========
Hibernate: 
    /* insert for
        jpa_basic.ex1_hello_jpa.entity.Team */insert 
    into
        Team (name, TEAM_ID) 
    values
        (?, default)
Hibernate: 
    /* update
        for jpa_basic.ex1_hello_jpa.entity.Member */update Member 
    set
        city=?,
        name=?,
        number=?,
        roleType=?,
        street=?,
        TEAM_ID=?,
        zipcode=? 
    where
        MEMBER_ID=?
Hibernate: 
    select
        m1_0.MEMBER_ID,
        m1_0.city,
        m1_0.name,
        m1_0.number,
        m1_0.roleType,
        m1_0.street,
        m1_0.TEAM_ID,
        m1_0.zipcode 
    from
        Member m1_0 
    where
        m1_0.MEMBER_ID=?
class jpa_basic.ex1_hello_jpa.entity.Team$HibernateProxy$q7Fz4jvQ
Hibernate: 
    select
        t1_0.TEAM_ID,
        t1_0.name 
    from
        Team t1_0 
    where
        t1_0.TEAM_ID=?
team2

안녕하세요 위 코드에 대해 질문 있습니다.

findMember.getTeam().getClass() 출력결과가
class jpa_basic.ex1_hello_jpa.entity.Team$HibernateProxy$q7Fz4jvQ 와 같습니다.

그리고 findMember2.getTeam().getClass(); 출력결과가 class jpa_basic.ex1_hello_jpa.entity.Team$HibernateProxy$q7Fz4jvQ 와 같습니다.

중간에 clear()를 하여 영속성 컨텍스트를 비웠는데 왜 프록시 객체를 조회하면 똑같은 프록시 객체가 나오는지 궁금합니다.

 

 

 

답변 2

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 감바스님

프록시가 toString()으로 생성하는 이름은 같아 보이지만 사실은 다른 객체입니다.

대상의 구조가 비슷하기 때문에 프록시 입장에서 이름을 생성할 때 비슷하게 만드는 것 같아요.
(이 이름을 어떤 룰로 만드는지는 저도 잘 모르겠습니다.)

하지만 중요한 사실은 실제로 둘은 전혀 다른 인스턴스입니다.

다음 코드를 넣어서 확인해보시면 false를 확인하실 수 있을거에요.

System.out.println(findMember.getTeam() == findMember2.getTeam());

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

감바스님의 질문은 em.flush()em.clear() 이후에 프록시 객체를 조회할 때, 왜 똑같은 프록시 객체가 나오는지에 대한 것입니다. JPA에서 flush() 메서드는 영속성 컨텍스트의 변경사항을 데이터베이스에 반영합니다. 그리고 clear() 메서드는 영속성 컨텍스트를 완전히 초기화합니다. 때문에, clear() 이후에 다시 엔티티를 조회하게 되면, JPA는 해당 엔티티를 데이터베이스에서 새로 조회하여 영속성 컨텍스트에 로드하게 됩니다.

프록시 객체가 반환되는 이유는 JPA가 getTeam() 같은 연관 관계가 지연 로딩(Lazy Loading)으로 설정되어 있을 때, 실제 엔티티 대신 프록시 객체를 사용하여 불필요한 쿼리 실행을 최소화하기 위해서입니다. 처음 findMember.getTeam().getClass()를 호출하면, Team 엔티티에 대한 프록시 객체가 생성되고, 실제 데이터에 접근하는 순간 (예: getName() 메서드 호출 시) 해당 프록시 객체가 초기화되며 데이터베이스에서 실제 Team 엔티티 데이터를 로드합니다.

감바스님의 질문 상황에서 clear()를 통해 영속성 컨텍스트를 초기화하더라도, 다시 엔티티에 접근하게 되면, 필요에 따라 다시 프록시 객체가 생성되고 데이터베이스에서 데이터를 로드하게 됩니다. 따라서 프록시 객체는 비록 같은 클래스 타입(jpa_basic.ex1_hello_jpa.entity.Team$HibernateProxy$q7Fz4jvQ)을 가지지만 다른 데이터를 가진 새로운 인스턴스일 수 있습니다.

이와 유사한 내용의 질문에서도 같은 이해를 돕는 답변을 제공하고 있습니다:
- 질문 제목: “프록시 초기화”
- 링크: https://www.inflearn.com/questions/538088

답변 내용에서는 getTeam() 호출이 프록시 초기화에 영향을 주지 않고, 실제 프록시 객체의 데이터에 접근하는 시점에 프록시 초기화가 발생하는 점에 대해 설명하고 있습니다.

이렇게 JPA에서 프록시 객체와 관련된 동작 방식을 이해하는 것은 영속성 컨텍스트 관리와 데이터 접근 최적화에 중요한 부분입니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

감바스님의 프로필 이미지
감바스

작성한 질문수

질문하기