public void printUserAndTeam(String memberId) {
Member member = em.find(Member.class, memberId);
Team team = member.getTeam();
System.out.println("회원 이름: " + member.getUsername());
System.out.println("소속팀: " + team.getName());
}
public void printUser(String memberId) {
Member member = em.find(Member.class, memberId);
Team team = member.getTeam();
System.out.println("회원 이름: " + member.getUsername());
}
=> 회원만 출력해도 되는 상황인데도 팀도 같이 조회하게 되면 손해라고 할 수 있음
(팀, 회원이 있으나 회원만 다루고 팀은 어쩌다 사용하는 비즈니스 로직이 있다면 더더욱)
프록시 기초
Member findMember = em.getReference(Member.class, member.getId());
System.out.println("findMember = " + findMember.getClass()); // 1
System.out.println("findMember.id = " + findMember.getId()); // 2
System.out.println("findMember.username = " + findMember.getUsername()); // 3
// 1. HibernateProxy~~ 클래스로 나옴
// 2. 이미 id값을 가지고 getReference를 하여 프록시 객체를 생성하였기 때문에 SQL 없이도 가져옴
// 3. username 은 프록시 객체에 없는 데이터이므로 실제 데이터베이스를 조회 (쿼리가 생성, 호출됨)
프록시 특징
프록시 객체의 초기화
Member member = em.getReference(Member.class, "id1");
member.getName();
프록시의 특징 (아주 중요)
Member m1 = em.find(Member.class, member1.getId());
Member m2 = em.find(Member.class, member2.getId());
Member m3 = em.getReference(Member.class, member3.getId());
System.out.println("m1 == m2" + (m1.getClass() == m2.getClass)); // true 반환
System.out.println("m1 == m3" + (m1.getClass() == m3.getClass)); // false 반환
System.out.println("m1 == m3" + (m3 instanceOf Member)); // true 반환
Member m1 = em.find(Member.class, member1.getId());
// 이미 영속성 컨텍스트에 올라감
Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference = " + reference.getClass()); // proxy 객체가 아닌 Member 호출
System.out.println("a == a: " + (m1 == reference)); // JPA 동작 매커니즘 상 true로 보장
Member reference = em.getReference(Member.class, member1.getId());
System.out.println("reference = " + reference.getClass());
em.detach(reference); // 준영속 상태로 해제 (clear, close 등...)
reference.getUsername(); // LazyInitializationException 발생
프록시 확인
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.LAZY) // team을 프록시로 조회
@JoinColumn(name = "TEAM_ID)
private Team team;
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.EAGER) // 함께 조회
@JoinColumn(name = "TEAM_ID)
private Team team;
JPA 구현체는 가능하면 조인을 사용해서 SQL 한번에 함께 조회
프록시와 즉시 로딩 시 주의 (가장 중요함)
List<Member> members = em.createQuery("select m from Member m", Member.class).getResultList();
// JPQL 작성 시 SQL로 번역되어서 쿼리 발생 -> 1번
// 위의 쿼리로 Member를 조회해보니 Team이 즉시 로딩이 걸려 있음 -> 반드시 가져와야 됨 -> 한번 더 조회 (쿼리 또 발생)
// Member만 가져오려고 했지만 (1번) 쿼리가 2번 발생
지연 로딩 활용 - 실무
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
// cascade를 하지 않았을 경우 예시
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
parent.addChild(child1);
parent.addChild(child2);
// 3번 persist를 진행해야 함
em.persist(parent);
em.persist(child1);
em.persist(child2);
// cascade를 사용할 경우
Child child1 = new Child();
Child child2 = new Child();
Parent parent = new Parent();
// 부모를 persist를 해도 자식 객체가 전부 영속화됨
em.persist(parent);
@OneToMany(mappedBy="parent", orphanRemoval = true)