김영한 님의 '자바 ORM 표준 JPA 프로그래밍 - 기본편'를 듣고 적은 글 입니다.
프록시(Proxy)는 내부 네트워크에서 인터넷 접속을 할 때에, 빠른 액세스나 안전한 통신등을 확보하기 위한 중계서버를 "프록시 서버"라고 일컫는다. 클라이언트와 Web서버의 중간에 위치하고 있어, 대신 통신을 받아 주는 것이 프록시 서버이다.
em.find() : 데이터베이스를 통해 실제 엔티티 객체 조회. 데이터베이스에 쿼리 나감em.getReference() : 데이터베이스 조회를 미루는 가짜 엔티티 객체 조회. 호출 시점에는 데이터베이스에 쿼리가 날아가지 않지만 필요할 때에 데이터베이스에 쿼리 나감
instanceof를 사용해야 함em.getReference() 를 사용해도 실제 엔티티 반환Member member = new Member();
member.setName("hello");
em.persist(member);
em.flush();
em.clear();
Member findMember1 = em.find(Member.class, member.getId());
System.out.println("member = " + findMember1.getClass());
Member findMember2 = em.getReference(Member.class, member.getId());
System.out.println("findMember = " + findMember2.getClass());
System.out.println("member == findMember : " + (findMember1 == findMember2));
tx.commit();

findMember2의 경우 getReference()를 사용했음에도 프록시 객체가 아닌 member 객체인 것을 확인할 수 있다.
이는 두가지 이유가 있는데 이미 member가 1차 캐시에 올라와있기에 프록시보다 원본 객체를가져오는 것이 성능 최적화에 적합하기 때문이다.
두번째 이유는 영속성 컨텍스트에 존재 시 프록시 객체를 사용하지 않는 다는 것이다. 이는 a==a의 결과를 항상 true로 반환하기 위함이다.
org.hibernate.LazyInitializationException 예외 발생em.detach(), em.close() 사용시 )PersistenceUnitUtil.isLoaded(Object entity)entity.getClass().getName() 출력시 ...HibernateProxy... 출력됨org.hibernate.Hibernate.initialize(entity);JPA에서 데이터를 조회할 때에 즉시 로딩과 지연 로딩 두가지 방식이 존재한다. 이때 FetchType을 사용해 연관관계에 있는 객체들을 어떻게 가져올 것인지에 대해 설정한다.
JPA는 사용자가 직접 쿼리를 생성하지 않고, JPA에서 JPQL을 사용해 쿼리문을 생성하기에 객체와 필드를 보고 쿼리를 생성한다. 그래서 다른 객체와 연관관계 매핑이 되어있으면 그 객체들까지 조회하게 되는데 이때 그 객체를 어떻게 불러올 것인지 설정할 수 있다.
지연 로딩은 필요한 시점에 연관된 객체의 데이터를 불러오는 것을 말한다.
지연 로딩을 사용 시 프록시 객체를 제공하기에 당장 데이터베이스에 쿼리가 나가지 않고 데이터를 실질적으로 사용하는 시점에 데이터베이스에 쿼리가 나간다.
@Entity
public class Member{
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
public class JpaMain{
...
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName();
...
}
위 코드의 경우 em.find() 까지만 해도 데이터베이스에 실질적인 쿼리가 나가지 않는다. 실제 team을 사용하는 시점인 team.getName()때에 데이터베이스에 쿼리가 나가고 데이터를 조회한다.
즉시 로딩은데이터를 조회할 때, 연관된모든 객체의 데이터까지 한 번에 불러오는 것을 말한다.
@Entity
public class Member{
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
public class JpaMain{
...
Member member = em.find(Member.class, 1L);
Team team = member.getTeam();
team.getName();
...
}
JPA는 조인을 사용해 member조회 시에 team도 항상 함께 조회한다.
이때 터미널에서 쿼리를 살펴보면 멤버를 조회하는 쿼리와 팀을 조회하는 쿼리 총 두번의 쿼리가 나가는 것을 확인할 수 있다.
만약 Member을 조회하는 JPQL을 날렸는데 연관된 Team이 1000개라면 이때 Team을 조회하는 SQL 쿼리가 1000개가 나가기 때문에 성능이 저하될 수 있다.
@ManyToOne, @OneToOne은 기본이 즉시 로딩이기에 LAZY로 설정 필요@...ToOne은 기본이 즉시 로딩이라고 생각하면 됨@OneToMany, @ManyToMany는 기본이 지연 로딩