단순히 member의 정보만을 조회하는 비지니스 로직이 존재할 경우 Team도 항상 조회할 필요성이 있을까? - 단순히 member의 정보만을 확인하면 되는데 Team도 함께 조회 시 성능상 비효율적이다. - 지연로딩 LAZY를 사용해서 member만을 불러와 Team을 불러오지 않고 member만 참조할 수 있다.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY) // 지연로딩
@JoinColumn(name="team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
}
김영한의 자바 ORM 표준 JPA 프로그래밍
지연 로딩 LAZY을 사용해 프록시로 조회
Member만을 조회하고 Team 관련 값들을 호출하지 않았을 경우 Member만을 조회한다.|
Member member1 = new Member();
member1.setUserName("hello1");
em.persist(member1);
Team team = new Team();
team.setName("teamA");
member1.setTeam(team);
em.persist(team);
em.flush();
em.clear();
Member m1 = em.find(Member.class, member1.getId());
김
Team의 값을 실제 불러왔을 경우에 비로소 Team의 값을 조회한다.
Team team = member.getTeam();
team.getName(); // 실제 team을 사용하는 시점에 초기화(DB조회)
즉시 로딩 EAGER를 사용해서 함께 조회
지연 로딩과 달리 Member와 Team을 자주 함께 사용할 경우 사용한다.
Member를 조회 시 항상 Team도 조회가 된다.
JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회한다.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "member_id")
private Long id;
@ManyToOne(fetch = FetchType.EAGER) // 지연로딩
@JoinColumn(name="team_id")
private Team team;
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "team_id")
private Long id;
private String name;
}
김영한의 자바 ORM 표준 JPA 프로그래밍
프록시와 즉시로딩 주의점
가급적 지연로딩만을 사용해야 한다. 왜냐하면, 즉시 로딩을 적용 시 예상치 못한 SQL을 발생하기 떄문이다.
즉시 로딩은 JPQL에서 N + 1 문제를 일으킨다.
@ManyToOne, @OneToOne은 기본이 즉시 로딩이므로 필히 LAZY(지연)로 설정이 필요하다.
@OneToMany, @ManyToMany는 기본이 지연 로딩이다.
지연, 즉시 로딩의 활용이 가능하나 모든 연관관계를 우선적으로 지연 로딩으로 사용해야 한다.
즉시 로딩이 필요 시 JPQL fetch 조인이나, 엔티티 그래프 기능을 사용해야 한다.
List<Member> members = new ArrayList<>();
members = em.createQuery("select m from Member m left join fetch m.team", Member.class).getResultList();
영속성 전이 : CASCADE
특정 엔티티를 영속 상태로 만들 때 연관된 엔티티도 함께 영속 상태로 만들고 싶을 때 사용한다. (예 : 부모 엔티티를 저장할 때 자식 엔티티도 함께 저장한다.) 김영한의 자바 ORM 표준 JPA 프로그래밍
영속성 전이 : 저장
영속성 전이는 연관관계를 매핑하는 것과는 아무 관련이 없다.
엔티티를 영속화할 때 연관된 엔티티도 함께 영속화하는 편리함 때문에 제공이 되는 것 뿐이다.
CASCADE의 타입 종류 : ALL(모두적용), PERSIST(영속), REMOVE(삭제), MERGE(병합) , REFRESH(REFRESH), DETACH(DETACH)가 있다.
실제 사용하기 위해서는 ManyToOne관계에 영속성 전이를 영속타입으로 설정한다.
@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
@Column(name = "child_id")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
부모 엔티티인 parent에 대해서만 persist를 진행해도 자식들도 영속이기 때문에 함께 저장된다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
고아 객체
고아 객체는 부모엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 것을 의미한다.
참조가 제거된 엔티티는 다른 곳에서 참조하지 않는 고아 객체로 판단해 삭제하는 기능이다.
고아 객체는 참조하는 곳이 하나일 때 사용해야 한다. 즉, 특정 엔티티가 개인 소유할 때 사용해야 한다.
@OneToOne, @OneToMany만 고아객체를 사용할 수 있다.
관계형 데이터베이스 관점으로 보았을 경우 부모를 제거 시 자식은 고아가 된다. 따라서 고아 객체 제거 기능을 활성화 시 부모를 제거할 때 자식도 함께 제거되는 것이다. 이것은 CascadeType.REMOVE처럼 동작한다.
아 객체도 연관관계 설정 시 엔티티에 해당 속성을 사용한다.
@Entity
@Getter
@Setter
public class Parent {
@Id @GeneratedValue
@Column(name = "parent_id")
private Long id;
private String name;
@OneToMany(mappedBy = "parent", orphanRemoval = true) // 고아 객체 설정
private List<Child> childList = new ArrayList<>();
// 양방향 연관관계 편의 함수
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
@Entity
public class Child {
@Id @GeneratedValue
@Column(name = "child_id")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
고아 객체 설정 후 부모를 삭제 시 자식도 함께 삭제된다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
em.persist(child1);
em.persist(child2);
em.flush();
em.clear();
Parent findParent = em.find(Parent.class, parent.getId());
em.remove(findParent);
영속성 전이 + 고아 객체, 생명주기
Cascade.Type.ALL + orphanRemoval=true를 조합 해 생명주기를 관리할 수 있다.
@Entity
@Getter
@Setter
public class Parent {
@Id @GeneratedValue
@Column(name = "parent_id")
private Long id;
private String name;
// 영속성 전이 + 고아 객체 설정
@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Child> childList = new ArrayList<>();
// 양방향 연관관계 편의 함수
public void addChild(Child child) {
childList.add(child);
child.setParent(this);
}
}
@Entity
public class Child {
@Id @GeneratedValue
@Column(name = "child_id")
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "parent_id")
private Parent parent;
}
스스로 생명주기를 관리하는 엔티티는 em.persist()로 영속화하고 em.remove()로 제거한다.
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
// 영속성 전이기능 활성화로 인한 생략 가능
// em.persist(child1);
// em.persist(child2);
em.flush();
em.clear();
Parent findParent = em.find(Parent.class, parent.getId());
em.remove(findParent);
// 고아 객체 기능 활성화로 생략 가능
//findParent.getChildList().remove(0);
영속성 전이와 고아 객체 옵션을 활성화 시 부모 엔티티를 통해 자식의 생명주기를 관리할 수 있다.