[JPA] 8. 프록시와 연관관계 관리 - 즉시 로딩, 지연로딩, 영속성 전이
[JPA] 8. 프록시와 연관관계 관리
자바 ORM 표준 JPA 프로그래밍 공부 기록
📍 8.2 즉시 로딩과 지연 로딩
프록시 객체는 주로 연관된 엔티티를 지연 로딩 할때 사용한다.
JPA는 연관된 엔티티의 조회 시점을 선택할 수 있도록 지연로딩
, 즉시로딩
두 가지 방법을 제공한다.
💭 지연 로딩 (LAZY
를 사용해서 프록시로 조회)
Member 엔티티
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String userName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="TEAM_ID")
private Team team;
...
테스트 코드
Team t = new Team();
t.setName("teamA");
em.persist(t);
Member m = new Member();
m.setUserName("HELLO");
m.setTeam(t);
em.persist(m);
em.flush();
em.clear();
Member findMember = em.find(Member.class, m.getId());
System.out.println(findMember.getTeam().getClass());
System.out.println("===========================");
System.out.println(findMember.getTeam().getName()); // 실제 team을 사용하는 시점에 초기화(DB 조회)
tx.commit();
출력 결과
Hibernate:
select
member0_.id as id1_3_0_,
member0_.TEAM_ID as TEAM_ID3_3_0_,
member0_.userName as userName2_3_0_
from
Member member0_
where
member0_.id=?
class com.jpa.db.Team$HibernateProxy$4y1KwunM //반환된 객체는 프록시 객체
===========================
Hibernate:
select
team0_.id as id1_5_0_,
team0_.name as name2_5_0_
from
Team team0_
where
team0_.id=?
teamA
Member findMember = em.find(Member.class, m.getId());
호출하면Member
만 조회하고Team
은 조회하지 않는다.
대신Team
멤버변수에프록시 객체
를 넣어둔다.
findMember.getTeam().getClass()
시 반환된 객체는 프록시 객체이다. 이 객체는 실제 사용될 때 까지 데이터 로딩을 하지 않는다. => 지연 로딩
조회한 Team
엔티티를 실제 사용하는 시점에 JPA가 SQL를 호출해서 팀 엔티티를 가져온다.
💭 즉시 로딩 (EAGER
를 사용해서 함께 조회)
Member와 Team을 자주 함께 사용한다면? 즉시 로딩 (EAGER
) 를 사용해서 함께 조회한다
Member 엔티티
@Entity
public class Member {
@Id
@GeneratedValue
private Long id;
private String userName;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name="TEAM_ID")
private Team team;
...
테스트 코드
Team t = new Team();
t.setName("teamA");
em.persist(t);
Member m = new Member();
m.setUserName("HELLO");
m.setTeam(t);
em.persist(m);
em.flush();
em.clear();
Member findMember = em.find(Member.class, m.getId()); //조인을 사용해서 SQL 한번에 함께 조회
System.out.println(findMember.getTeam().getClass());
System.out.println("===========================");
System.out.println(findMember.getTeam().getName());
tx.commit();
출력 결과
Hibernate:
select
member0_.id as id1_3_0_,
member0_.TEAM_ID as TEAM_ID3_3_0_,
member0_.userName as userName2_3_0_,
team1_.id as id1_5_1_,
team1_.name as name2_5_1_
from
Member member0_
left outer join
Team team1_
on member0_.TEAM_ID=team1_.id
where
member0_.id=?
class com.jpa.db.Team
===========================
teamA
Member findMember = em.find(Member.class, m.getId());
호출 시Member
뿐만 아니라Team
도 함께 조회한다. 이 때 Member와 Team을 조인해서 쿼리 한 번으로 두 엔티티를 모두 조회한다.
💭 즉시 로딩 주의
- 실무에서 가급적 지연 로딩 사용
즉시 로딩은 예상치 못한 SQL이 발생한다
또한 JPQL에서 N+1 문제를 일으킨다 @ManyToOne
,@OneToOne
은 기본이 즉시로딩이다 =>LAZY
로 변경
💭 지연 로딩 활용
- 모든 연관관계에 지연 로딩을 활용
- 실무에서는 즉시 로딩 사용 X
📍 8.4 영속성 전이 : CASCADE
연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 CASCADE
옵션을 사용하면 된다.
ex) 부모 엔티티 저장 시 자식 엔티티도 함께 저장
Parent, Child Entity
@Entity
public class Parent {
@Id
@GeneratedValue
@Column(name="PARENT_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
private List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
...
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="PARENT_ID")
private Parent parent;
테스트 코드
Child c1 = new Child();
Child c2 = new Child();
Parent p = new Parent();
p.addChild(c1);
p.addChild(c2);
//부모 저장, 연관된 자식도 함께 저장
em.persist(p);
💭 CASCADE 옵션 종류
• ALL
: 모두 적용
• PERSIST
: 영속
• REMOVE
: 삭제
• MERGE
: 병합
• REFRESH
: REFRESH
• DETACH
: DETACH
📍 8.5 고아 객체
부모 엔티티와 연관 관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능
=> 고아 객체(ORPHAN) 제거
부모 엔티티의 컬렉션에서 자식 엔티티의 참조만 제거하면 자식 엔티티가 자동으로 삭제
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent", orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
childList
컬렉션에서 제거한 엔티티는 자동으로 삭제된다고 한다.
=> 그러나 테스트 해봤을 때 orphanRemoval = true
만으로는 컬렉션에서 첫 번째 자식을 제거했을 때 데이터베이스의 데이터가 삭제 되지 않았다.
관련 자료 : https://github.com/jyami-kim/Jyami-Java-Lab/issues/1
부모를 제거하면 자식은 고아가 된다. 따라서 orphanRemoval = true
하면, 부모를 제거할 때 자식도 함께 제거된다. 이것은 CascadeType.REMOVE
처럼 동작한다.
📍 8.6 영속성 전이 + 고아 객체, 생명주기
CascadeType.ALL
+ orphanRemoval = true
을 둘다 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
- 자식을 저장하려면 부모에 등록만 하면 된다 (
CASCADE
) - 자식을 삭제하려면 부모에서 제거하면 된다 (
orphanRemoval
)
Author And Source
이 문제에 관하여([JPA] 8. 프록시와 연관관계 관리 - 즉시 로딩, 지연로딩, 영속성 전이), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jodawooooon/JPA-8.2-즉시-로딩과-지연-로딩저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)