Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- orm
- 백엔드
- ec2
- rds
- DynamoDB
- spring
- serverless
- MSA
- goorm
- Docker
- CodeCommit
- sqs
- kakao
- backenddeveloper
- 자격증
- CICD
- Redis
- backend
- 개발자
- s3
- QueryDSL
- codebuild
- Spring Boot
- 오블완
- mapping
- jpa
- 티스토리챌린지
- codedeploy
- 스터디
- aws
Archives
- Today
- Total
gony-dev 님의 블로그
[JPA] 프록시와 연관관계 본문
Proxy
프록시(Proxy)는 '대리'라는 의미로 인터넷 관점에서는 내부 네트워크에서 인터넷 접속을 할 때, 빠른 액세스나 안전한 통신 등을 확보하기 위한 중계서버를 일컫는다.
클라이언트와 웹 서버의 중간에 위치하고 있기에, 이 통신을 받아주는 것을 '프록시 서버'라고 부른다.
그렇다면 JPA 관점에서의 프록시란 뭘까?
- "em.find()"와 "em.getReference()", EntityManager에 대한 두 가지 메서드가 여기 있다. 이 둘은 조회라는 관점에서는 비슷하지만 조회를 하는 방식이 다르다.
- "em.find()"는 데이터베이스를 통해서 실제 엔티티 객체를 조회한다.
- "em.getReference()"는 데이터베이스 조회를 미루는 프록시 엔티티 객체를 조회한다.
em.getReference()
- 해당 기능은 데이터베이스 조회 시 가짜 객체를 던져준다.
- 프록시(가짜)의 특징
- 실제 클래스를 상속 받아서 만들어진다.
- 실제 클래스와 겉모양이 같다.
- 사용하는 입장에서는 진짜의 유무를 판단하지 않고 사용하면 된다!
- 프록시 객체는 실제 객체의 참조를 보관한다.
- 프록시 객체 호출 시 프록시 객체는 실제 객체의 메서드를 호출한다.
Member member = new Member();
member.setUsername("memberA");
em.persist(member);
em.flush();
em.clear();
Member findMember = em.getReference(Member.class, member.getId()); // 프록시 객체 호출
System.out.println("findMember = " + findMember.getClass());
System.out.println("findMember.id = " + findMember.getId());
System.out.println("findMember.username = " + findMember.getUsername());
Hibernate:
/* insert hello.jpa.Member
*/ insert
into
Member
(id, createdBy, createdDate, lastModifiedBy, lastModifiedDate, age, description, locker_id, roleType, name)
values
(null, ?, ?, ?, ?, ?, ?, ?, ?, ?)
findMember = class hello.jpa.Member$HibernateProxy$yJgMgbkR
findMember.id = 1
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.username = memberA
- 위의 메서드와 로그를 보면 findMember.getClass()로 확인된 객체가 Member 객체가 아니라, 하이버네이트가 강제로 만든 가짜 프록시 객체인 것을 알 수 있다.
- 프록시 특징 Part.2
- 프록시 객체는 처음 사용할 때 한 번만 초기화되며, 초기화 시에는 프록시 객체가 실제 엔티티가 되는 것이 아닌 프록시 객체를 통해 실제 엔티티에 접근이 가능한 것이다.
- 프록시 객체는 원본 엔티티를 상속받기에 타입 체크에 유의해야 한다.
- 영속성 컨텍스트에 찾는 엔티티가 이미 있다면 em.getReference()를 호출해도 실제 엔티티를 반환한다.
- 이미 영속성 컨텍스트에 있는 객체를 굳이 다시 프록시로 감싸서 반환하는 게 의미가 없기 때문이다.
- 영속성 컨텍스트의 도움을 받을 수 없는 준영속 상태이면, 프록시를 초기화 할 때 문제가 발생하니 주의해야 한다.
즉시 로딩과 지연 로딩
멤버를 조회할 때 연관관계로 얽혀 있는 Team이라는 엔티티가 있다면, 이를 함께 조회하는 것이 괜찮을까?
우리는 한 엔티티에 얽혀 있는 연관관계를 필요 시에 조회할지, 아니면 항시 조회 가능 상태로 만들지 정할 수 있다.
이것이 바로 즉시 로딩과 지연 로딩이다.
- 본론부터 말하자면 필요 시에 함께 조회를 할 경우에는 지연 로딩을 사용하면 되고, 항시 조회 가능 상태로 만든다면 즉시 로딩을 사용하면 된다.
지연 로딩 LAZY
- 지연 로딩으로 설정할 시 연관된 엔티티를 프록시로 가져온다.
- 즉시 로딩을 사용할 경우, SQL 문제가 일어나게 되는데,
- 즉시 로딩은 JPQL에서 N+1문제를 일으킨다.
- 실무에서도 모든 관계에서 지연 로딩을 사용할 것을 당부하며, JPQL fetch 조인(성능 최적화 필요시)이나, 엔티티 그래프 기능을 사용하는 것을 지향하고 있다.
'Spring > JPA' 카테고리의 다른 글
[JPA] 고급 매핑 (0) | 2024.11.10 |
---|---|
[JPA] 자바 ORM 표준 JPA 프로그래밍 - 다양한 매핑관계 (1) | 2024.09.04 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 - 엔티티 매핑 (0) | 2024.08.28 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 - 영속성 관리(플러시와 준영속 상태) (0) | 2024.08.12 |
[JPA] 자바 ORM 표준 JPA 프로그래밍 - 영속성 관리 (0) | 2024.08.08 |