
위와 같은 상황에서 Member를 조회할 때 Team도 같이 DB에서 조회를 해야할까?
Member만 사용하고 Team은 사용 안하는 경우 둘다 조회하면 손해다.
JPA는 이것을 지연 로딩과 프록시로 해결한다.
em.find() : 데이터베이스를 통해 실제 엔티티 객체 조회
em.getReference() : 데이터베이스 조회를 미루는 가짜 엔티티 객체 조회
getReference()를 호출하는 시점에는 db에 쿼리를 안한다.
그 값이 실제 사용되는 시점에는 쿼리가 나간다.
getReference()는 껍데기는 똑같은데 안이 텅텅 빈 프록시 객체를 반환함. 그리고 target이 진짜 엔티티 객체를 가리킴
프록시는 실제 엔티티 객체를 상속해서 만들어진다.
실제 클래스와 겉 모양이 같다.
사용하는 입장에서는 진짜 객체인지 프록시 객체인지 구분하지 않고 사용하면 된다.
프록시 객체는 실제 객체의 참조 target을 보관한다.
프록시 객체를 호출하면 프록시 객체는 실제 객체의 메소드를 호출한다.

처음에 getName을 호출하면 target에 값이 없기 때문에 영속성 컨텍스트에 초기화를 요청한다. 영속성 컨텍스트가 DB를 조회해서 실제 Entity 객체를 생성하고 target과 연결해준다.
== 비교가 아닌 instance of 를 사용해야함 instance of를 사용해라m1 instanceof Memberem.getReference()를 호출해도 실제 엔티티를 호출한다. == 비교하면 항상 true가 되어야 함em.find()해도 프록시를 조회한다em.detach(refMember)LazyInitializationExceptionPersistenceUnitUtil.isLoaded(Object entity)Hibernate.initalize(refMember) 로 강제 초기화 가능 다시 예시로 돌아가서 Member만 조회할 것인데 Team 까지 조회하면 손해다. JPA는 지연로딩이라는 옵션을 제공한다
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn
private Team team;
이렇게 할 경우 Team은 프록시객체로 조회하고 Member만 db에서 조회한다. 지연 로딩으로 설정하면 연관된 객체를 프록시로 가져온다.
team.getName()으로 실제 Team을 사용하는 시점에 Team이 초기화된다.
비즈니스 로직 상 Member를 조회했을 때 Member만 사용하고 Team은 거의 쓰지 않는다면 지연 로딩으로 설정하는게 맞다.
Member와 Team을 자주 함께 사용할 수도 있다. 그러면 즉시로딩으로 바꿀 수 있다.
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn
private Team team;
설정값을 Eager로 주면 즉시 로딩을 한다.
한번에 진짜 객체를 전부 가져온다.
즉 Member를 조회할 때 Team까지 같이 가져온다.
대부분의 JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회한다.
@ManyToOne @OneToOne은 기본이 즉시 로딩이다. @OneToMany @ManyToMany는 기본이 지연 로딩이다특정 엔티티를 영속 상태로 만들 때 연관된 엔티티를 함께 영속 상태로 만들고 싶을 때 사용한다.
@Entity
public class Parent {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "parent")
List<Child> childList = new ArrayList<>();
public void addChild(Child child){
childList.add(child);
child.setParent(this);
}
}
addChild는 연관관계 편의 메소드이다.
@Entity
public class Child {
@Id
@GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="parent_id")
private Parent parent;
}
이런 경우 Parent 하나에 Child 객체 2개가 있을 경우, 아래와 같이 영속성 컨텍스트에 모두 persist 해줘야 한다.
Parent parent = new Parent();
Child child1 = new Child();
Child child2 = new Child();
parent.addChild(child1);
parent.addChild(child2);
em.persist(parent);
em.persist(child1);
em.persist(child2);
Parent 중심으로 코드를 작성하는데 Child까지 persist 해야 하는 귀찮은 상황이 발생한다.
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
List<Child> childList = new ArrayList<>();
Parent에서 위와 같이 수정해준다.
그러면 em.persist(parent)만 해도 child1,child2가 persist 된다.
연관관계와 상관없이 Parent를 persist 할 때 Child까지 persist 하고 싶으면 Parent의 Child 필드에 cascade = CascadeType.ALL)를 걸어주면 된다.
Parent - Child 처럼 Parent가 Child 여럿을 관리하고 Child랑 관련된게 Parent 하나일 때는 이렇게 사용해도 된다.
하지만 Child랑 연관된 객체가 또 있으면 CASCADE를 사용하면 안된다.
고아 객체 제거는 부모 엔티티와 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하는 것이다.
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL,
orphanRemoval = true)
List<Child> childList = new ArrayList<>();
orpahnRemoval=true 속성으로 사용한다.
Parent가 관리하는 컬렉션 리스트에서 Child가 제거되면 Child에 대한 delete쿼리가 나간다.
@OneToOne , @OneToMany만 가능 CascadeType ALL + orphanRemoval=true 같이 사용하면?
em.persist()와 em.remove() 로 관리