Member엔티티가 각각 Team이라는 컬럼을 갖고있다. Team은 따로 엔티티로 존재한다.
그렇다면 Member 엔티티를 조회할 때 Team도 함께 조회해야 할까??
대리(행위)나 대리권, 대리인을 의미 -> 어떤 것을 대신해주는 것
em.find() vs em.getReference()
- em.find() : DB를 통해 실제 Entity 객체 조회
- em.getReference() : DB조회를 미루는 가짜(프록시) Entity객체 조회
@Entity
@Getter
@Setter
public class Member extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "name")
private String username;
private Integer age;
@Enumerated(EnumType.STRING)
private RoleType roleType;
@Lob
private String description;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
@OneToOne
@JoinColumn(name = "locker_id")
private Locker locker;
@OneToMany(mappedBy = "member")
private List<MemberProduct> memberProducts = new ArrayList<>();
}
Member member = new Member();
member.setCreatedBy("creator");
em.persist(member);
em.flush();
em.clear();
Member findMember = em.find(Member.class, member.getId());
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.username = " + findMember.getUsername());
tx.commit();
결과
Hibernate:
/* insert hello.jpa.Member
*/ insert
into
Member
(id, createdBy, createdDate, lastModifiedBy, lastModifiedDate, age, description, locker_id, roleType, name)
values
(null, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Hibernate:
select
member0_.id as id1_4_0_,
member0_.createdBy as createdB2_4_0_,
member0_.createdDate as createdD3_4_0_,
member0_.lastModifiedBy as lastModi4_4_0_,
member0_.lastModifiedDate as lastModi5_4_0_,
member0_.age as age6_4_0_,
member0_.description as descript7_4_0_,
member0_.locker_id as locker_10_4_0_,
member0_.roleType as roleType8_4_0_,
member0_.team_id as team_id11_4_0_,
member0_.name as name9_4_0_,
locker1_.id as id1_3_1_,
locker1_.name as name2_3_1_,
team2_.id as id1_8_2_,
team2_.createdBy as createdB2_8_2_,
team2_.createdDate as createdD3_8_2_,
team2_.lastModifiedBy as lastModi4_8_2_,
team2_.lastModifiedDate as lastModi5_8_2_,
team2_.name as name6_8_2_
from
Member member0_
left outer join
Locker locker1_
on member0_.locker_id=locker1_.id
left outer join
Team team2_
on member0_.team_id=team2_.id
where
member0_.id=?
findMember.id = 1
findMember.username = creator
프록시 객체
실제 Entity를 가리키는 target이라는 값만 들어있음
(최초의 target은 null값 보유)
이후 실제 객체에 접근하는 method(getter)를 호출하면 그 때 실제 DB에 접근해서 target에 실제Entity가 연결됨
1 ) em.getReference()로 호출하여 프록시 객체가 반환( target에는 null이 들어가 있음)
2 ) 실제 객체에 접근하는 member.getName() 호출
3 ) target이 null이기 때문에 영속성 컨텍스트에 요청
4 ) 영속성 컨텍스트는 실제 DB에서 조회한 후 target에 실제 Entity를 연결
5 ) 프록시 객체의 target을 통해 .getName() 호출됨
프록시 객체 초기화는 최초 사용시 처음에만 수행
프록시 객체 초기화시 프록시 객체가 실제 Entity로 바뀌는 것이 아니다.
->프록시 객체의 target에 실제 Entity가 연결될 뿐!
영속성 컨텍스트에 찾는 Entity가 이미 있으면, em.getReference()해도 실제 Entity가 반환됨
-> JPA는 동일 객체임을 보장하기 위해 프록시를 먼저 조회하면 프록시 객체로 맞추고, 아니면 실제 Entity에 맞추는 내부로직을 가지고 있음
(그래서 JPA는 하나의 트랜잭션에서 같은 객체의 조회는 항상 동일 객체를 보장함)
영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태에서 프록시 초기화 하면 오류 발생함
-> 결국 프록시는 영속성 컨텍스트를 통해 일어난다는 것이 핵심.
@ManyToOne(fetch = FetchType.LAZY)