프록시와 연관관계 관리

2025. 12. 26. 16:56·JPA

프록시

프록시 기초

em.find() vs em.getReference()

- em.find() : 데이터베이스를 통해서 실제 엔티티 객체 조회

- em.getReference() : 데이터베이스 조회를 미루는 가짜(프록시) 엔티티 객체 조회

 

프록시 특징

- 실제 클래스를 상속받아 만들어짐

- 실제 클래스와 겉 모양이 같다.

- 사용하는 입장에선 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 됨 (이론상)

- 프록시 객체는 실제 객체의 참조(target)을 보관

- 프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드 호출

- 프록시 객체는 처음 사용할 때 한번만 초기화한다. (그 이후엔 계속 가져다 씀)

- 프록시 객체를 초기화 할 때 프록시 객체를 통해서 실제 엔티티에 접근하는것이지 실제 엔티티로 바뀌는게 아니다.

- 프록시 객체는 원본 엔티티를 상속받기 때문에 타입 체크시 instanceof를 사용해야한다. (==는 false)

- 영속성 컨텍스트에 찾는 엔티티가 이미 있으면 getReference()를 호출해도 실제 엔티티를 반환한다. 

(영속성 컨텍스트에 이미 엔티티가 있으면 당연히 프록시를 호출할 필요가 없음)

- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태일 때, 프록시를 초기화하면 문제 발생

(영속성 컨텍스트를 통해 실제 엔티티를 생성하는 것이기 때문이다.)

 

프록시 확인

- 프록시 인스턴스의 초기화 여부 확인 : em.PersistenceUnitUtil.isLoaded() 사용

- 프록시 클래스 확인 방법 : entity.getCLass().getName()

- 프록시 강제 초기화 : org.hibernate.Hibernate.initialize(entity);

(참고로 하이버네이트의 기능이다. JPA 표준은 강제 초기화가 없음)

 

지연로딩

Member를 조회할때 Team도 함께 조회해야 할까?

 

단순히 member 정보만 사용하는 비즈니스 로직이라면

@Entity
public class Member {
	@Id
	@GeneratedValue
	private Long id;

	@Column(name = "USERNAME")
	private String name;

	@ManyToOne(fetch = FetchType.LAZY) //**
	@JoinColumn(name = "TEAM_ID")

	private Team team;
..}

 

지연로딩을 사용했을 때, Member member = em.find(Member.Class, 1L); 를 호출할 경우

Team이라는 프록시 객체가 생성된다. (실제 엔티티가 아님)

 

Team 프록시 객체는 실제 team을 사용하는 시점, 즉 프록시 내부 값을 실제로 접근 하는 순간 DB조회가 일어나면서 프록시가 초기화된다.

이렇게 Member만 호출할 수 있고, 필요시에 Team을 호출하게 할 수 있다.

 

Member와 Team을 자주 함께 사용하는 경우? (비권장)

fetch = FetchType.EAGER를 사용하면 된다.

지연로딩과 반대로 member 조회시 항상 team도 함께 조회 쿼리가 나간다.

 

즉시로딩 주의점

- 예상치 못한 SQL이 발생할 수 있음

- JPQL에서 N+1 발생

- @ManyToOne, @OneToOne은 EAGER가 기본값이니 LAZY로 설정해줘야 함

 

결론 : 지연 로딩을 사용해라, fetch join이나 엔티티 그래프 기능을 사용할 것

 

영속성 전이 - CASCADE

특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용한다.

위의 경우 em.persist(parent);, em.persist(child); 처럼 child를 일일이 영속화 시켜 줘야한다.

하지만 cascade를 사용하면 em.persist(parent);만 해줘도 같이 연결되어있는 child는 자동으로 영속화된다.

cascade는 연관관계를 매핑하는것과는 아무 관계가 없다. 

엔티티 영속화 과정에서 연관된 엔티티도 함께 영속화하는 편리함을 제공할 뿐이다.

 

고아객체

orphanRemoval = true

 

- 고아 객체 제거 : 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제

 

주의점

- 참조하는곳이 하나일 때 사용해야한다.

- 특정 엔티티가 개인 소유할 때만 사용할 것

- @OneToOne, @OneToMany만 가능하다.

 

영속성 전이 + 고아 객체, 생명주기

- CascadeType.ALL + orphanRemoval = true

스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화, em.remove()로 제거

- 두 옵션을 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.

- DDD 설계시 유용함.

'JPA' 카테고리의 다른 글

JPA의 값 타입  (0) 2025.12.27
고급 매핑 - 상속 관계, Mapped Superclass  (0) 2025.12.25
엔티티 매핑, 연관관계  (0) 2025.12.12
영속성 관리 - 내부 동작 방식  (0) 2025.12.05
데이터 접근 기술 - JPA  (0) 2025.10.24
'JPA' 카테고리의 다른 글
  • JPA의 값 타입
  • 고급 매핑 - 상속 관계, Mapped Superclass
  • 엔티티 매핑, 연관관계
  • 영속성 관리 - 내부 동작 방식
공부처음하는사람
공부처음하는사람
  • 공부처음하는사람
    lazzzykim
    공부처음하는사람
  • 전체
    오늘
    어제
    • 분류 전체보기 (159)
      • Kotlin (31)
      • Java (56)
      • Spring (44)
      • JPA (6)
      • Algorithm (3)
      • TroubleShooting (1)
      • 내일배움캠프 프로젝트 (14)
      • Setting (2)
      • ... (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
    • 글쓰기
  • 링크

  • 인기 글

  • 태그

    싱글톤
    spring
    빈 생명주기
    Di
    언체크예외
    김영한
    OCP
    김영한의 실전 자바
    트랜잭션
    내일배움캠프
    예외처리
    중첩클래스
    배열
    제네릭
    java
    jpa
    캡슐화
    kotlin
    다형성
    김영한의 실전자바
  • hELLO· Designed By정상우.v4.10.3
공부처음하는사람
프록시와 연관관계 관리
상단으로

티스토리툴바