자바 ORM 표준 JPA 프로그래밍 공부 기록
프록시 객체는 주로 연관된 엔티티를 지연 로딩 할때 사용한다.
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을 조인해서 쿼리 한 번으로 두 엔티티를 모두 조회한다.@ManyToOne
, @OneToOne
은 기본이 즉시로딩이다 => LAZY
로 변경연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 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);
• ALL
: 모두 적용
• PERSIST
: 영속
• REMOVE
: 삭제
• MERGE
: 병합
• REFRESH
: REFRESH
• DETACH
: DETACH
부모 엔티티와 연관 관계가 끊어진 자식 엔티티를 자동으로 삭제하는 기능
=> 고아 객체(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
처럼 동작한다.
CascadeType.ALL
+ orphanRemoval = true
을 둘다 활성화하면 부모 엔티티를 통해서 자식의 생명주기를 관리할 수 있다.
CASCADE
)orphanRemoval
)