[JPA] νλ‘μ(Proxy)μ μ§μ°λ‘λ©(Lazy Loading)
μ°κ΄κ΄κ³λ₯Ό κ°λ μ¬λ¬ μν°ν°κ° μμ λ ν μν°ν°λ₯Ό μ‘°νν λ λ°λμ μ°κ΄λ μν°ν°λ€μ΄ μ¬μ©λμ§λ μμ΅λλ€.
λ³Έλ‘ λΆν° λ§νμλ©΄, μ§μ° λ‘λ©μ μ¬μ©νλ©΄ Memberμν°ν°μ Team μν°ν°κ° λ€λμΌλ‘ λ§€νλμ΄ μμ λ, Member Entityλ₯Ό μ‘°ννλ€κ³ ν΄μ λ°λμ Team EntityκΉμ§ μ‘°νλμ§ μμ΅λλ€.
JPAλ μν°ν°κ° μ€μ μ¬μ©λ λκΉμ§ λ°μ΄ν°λ² μ΄μ€ μ‘°νλ₯Ό μ§μ°νλ λ°©λ²μ μ 곡ν©λλ€. μ΄λ₯Ό μ§μ° λ‘λ©(Lazy Loading)μ΄
λΌκ³ ν©λλ€.
- μ½κ² λ§ν΄ team.getName() μ²λΌ Team μν°ν°μ κ°μ μ€μ μ¬μ©νλ μμ μ Team Entityμ νμν λ°μ΄ν°λ₯Ό μ‘°νν©λλ€.
- μ§μ° λ‘λ© κΈ°λ₯μ μ€μ μν°ν° κ°μ²΄ λμ μ λ°μ΄ν°λ² μ΄μ€ μ‘°νλ₯Ό μ§μ°ν μ μλ κ°μ§ νλ‘μ κ°μ²΄λ₯Ό μ¬μ©ν©λλ€. νλ‘μμ λν΄ μμλ΄ μλ€.
νλ‘μ
μν°ν°λ₯Ό μ€μ μ¬μ©νλ μμ κΉμ§ λ°μ΄ν°λ² μ΄μ€ μ‘°νλ₯Ό λ―Έλ£¨κ³ μΆμ κ²½μ° EntityManager.getReference()λ₯Ό μ¬μ©ν΄μ νλ‘μ κ°μ²΄λ₯Ό λ§λ€ μ μμ΅λλ€.
Member member = em.getReference(Member.class,1L);
- memberλ μ€μ κ°μ²΄κ° μλ νλ‘μ κ°μ²΄μ λλ€.
- find() λ©μλμ λ¬λ¦¬ μ΄ λ©μλλ₯Ό νΈμΆν λ JPAλ μ€μ λ°μ΄ν°λ² μ΄μ€λ₯Ό μ‘°ννμ§λ, μ€μ μν°ν° κ°μ²΄λ₯Ό μμμ± μ»¨ν μ€νΈμ μμνμ§λ μμ΅λλ€.
- λμ λ°μ΄ν°λ² μ΄μ€ μ κ·Όμ μμν νλ‘μ κ°μ²΄λ₯Ό λ°νν©λλ€.
μ‘°κΈ ν·κ°λ¦¬λκΉ νλ¦μ ν λ² μ§κ³ λμ΄κ°κ² μ΅λλ€.
Member member = em.getReference(Member.class, 1L);
β‘οΈ Member νμ Proxy κ°μ²΄ μμ± (member)
// νλ‘μ κ°μ²΄ μ΅μ΄ μ¬μ© -> νλ‘μ κ°μ²΄ μ΄κΈ°ν μμ²
member.getName();
// μλ³μμ κ²½μ° μ΄κΈ°νλ₯Ό μμ²νμ§ μμ΅λλ€. λ€μμ μ€λͺ
member.getId();
νλ‘μ κ°μ²΄λ‘ μ€μ μν°ν° κ°μ²΄μ λ©μλ νΈμΆ
β‘οΈ μμμ± μ»¨ν μ€νΈμκ² μ€μ μν°ν°λ₯Ό μμ²ν©λλ€. (μ΄κΈ°ν μμ²)
β‘οΈ μμμ± μ»¨ν μ€νΈμ μλ κ²½μ°, μμμ± μ»¨ν μ€νΈλ DBλ₯Ό μ‘°ννμ§ μκ³ νλ‘μκ° μλ μμμ± μ»¨ν μ€νΈμ μ€μ Entityλ₯Ό λ°νν©λλ€. (μ€μ Entity μ¬μ©)
μ‘°ν λμμ΄ μμμ± μ»¨ν μ€νΈμ μ΄λ―Έ μμΌλ©΄ νλ‘μ κ°μ²΄λ₯Ό μ¬μ©ν μ΄μ κ° μμΌλ―λ‘ μ€μ μν°ν°λ₯Ό μ¬μ©ν©λλ€.
β‘οΈ μμμ± μ»¨ν μ€νΈμ μλ κ²½μ°, μμμ± μ»¨ν μ€νΈλ DBλ₯Ό μ‘°νν΄μ μ€μ Entityλ₯Ό νλ‘μ κ°μ²΄μ targetμ 리ν΄ν©λλ€. (μ€μ μν°ν° μμ± λ° μ°Έμ‘°λ₯Ό νλ‘μμ 보κ΄)
β‘οΈ targetμ getId()λ₯Ό νΈμΆν΄μ κ²°κ³Όλ₯Ό 리ν΄ν©λλ€. ( target.getId() νΈμΆ ) (νλ‘μ κ°μ²΄μ λ©μλ μ€ν -> μ€μ κ°μ²΄μ λ©μλ μ€ν)
νλ‘μμ νΉμ§
νλ‘μ ν΄λμ€λ μ€μ ν΄λμ€λ₯Ό μμλ°μ λ§λ€μ΄μ§λ―λ‘ μ€μ ν΄λμ€μ κ²λͺ¨μμ΄ κ°μ΅λλ€. μ¬μ©μλ μ΄ κ°μ²΄κ° νλ‘μ κ°μ²Έμ§, μ€μ μν°ν° κ°μ²΄μΈμ§ ꡬλΆνμ§ μμλ λ΄λΆμ μΌλ‘ νλ‘μλ‘ μ²λ¦¬νκΈ° λλ¬Έμ ꡬλΆν νμκ° μμ΅λλ€.
- νλ‘μ κ°μ²΄λ μ²μ μμ±μ ν λ²λ§ μ΄κΈ°νλ©λλ€.
- νλ‘μ κ°μ²΄κ° μ΄κΈ°ν λλ©΄ νλ‘μ κ°μ²΄λ₯Ό ν΅ν΄ (μλ°ν λ§νλ©΄ λ΄λΆμ μ€μ μν°ν°μ λ§€νλ targetμ ν΅ν΄) μ€μ μν°ν°μ μ κ·Όν μ μμ΅λλ€.
- μμμ± μ»¨ν μ€νΈμ λμμ λ°μμΌ κ°λ₯νλ―λ‘ μ€μμ μνμ μν°ν°λ₯Ό νλ‘μ κ°μ²΄λ‘ μμ±ν κ²½μ°, μμΈκ° λ°μν©λλ€.
νλ‘μμ μλ³μ(PK)
μν°ν°λ₯Ό νλ‘μλ‘ μ‘°νν λ μλ³μ(PK) κ°μ νλΌλ―Έν°λ‘ μ λ¬νλλ° νλ‘μ κ°μ²΄λ μ΄ μλ³μ κ°μ 보κ΄νκ³ μμΌλ―λ‘, μλ³μ κ°μ μ‘°ννλ team.getId()λ₯Ό νΈμΆν΄λ νλ‘μλ₯Ό μ΄κΈ°ν νμ§ μμ΅λλ€.
Team team = em.getReference(Team.class, 1L);
team.getId(); // μ΄κΈ°ν μμ² X
μν°ν° μ κ·Ό λ°©μμ @Access(AccessType.PROPERTY) λ‘ μ€μ ν κ²½μ°μλ§ μ΄κΈ°ν μμ² X
μν°ν° μ κ·Ό λ°©μμ @Access(AccessType.FIELD) λ‘ μ€μ ν κ²½μ°μλ μ΄κΈ°ν μμ² O
Member member = em.find(Member.class, 1L);
Team team = em.getReference(Team.class, 1L); // SQL μ€ν X
member.setTeam(team);
νλ‘μ κ°μ²΄λ μλ³μ κ°μ κ°μ§κ³ μκΈ° λλ¬Έμ μλ³μ κ°μ μ¬μ©νμ¬ μ°κ΄κ΄κ³λ₯Ό μ€μ ν λλ νλ‘μλ₯Ό μ΄κΈ°ννμ§ μμ΅λλ€.
νλ‘μλ₯Ό μ¬μ©νλ©΄ λ°μ΄ν°λ² μ΄μ€ μ κ·Όνμλ₯Ό μ€μΌ μ μμ΅λλ€.
μ°κ΄κ΄κ³λ₯Ό μ€μ ν λλ μν°ν° μ κ·Ό λ°©μμ νλλ‘ μ€μ ν΄λ νλ‘μλ₯Ό μ΄κΈ°ννμ§ μμ΅λλ€.
νλ‘μ μ΄κΈ°ν νμΈ
- JPAκ° μ 곡νλ PersistenceUnitUtil.isLoaded(Object entity) λ©μλλ₯Ό μ¬μ©νλ©΄ νλ‘μ μΈμ€ν΄μ€μ μ΄κΈ°ν μ¬λΆλ₯Ό νμΈν μ μμ΅λλ€.
boolean isLoad = em.getPersistenceUnitUtil().isLoaded(entity);
νλ‘μ/μ€μ κ°μ²΄ νμΈ
System.out.println("memberProxy = " + member.getClass().getName());
-> νλ‘μ κ°μ²΄μ κ²½μ° ...javassist..
μ§μ°λ‘λ©, μ¦μλ‘λ©
νλ‘μ κ°μ²΄λ μ£Όλ‘ μ°κ΄λ μν°ν°λ₯Ό μ§μ°λ‘λ©(μ€μ μν°ν°λ₯Ό μ¬μ©νλ κ²½μ°μλ§ λ°μ΄ν°λ² μ΄μ€λ₯Ό μ‘°ν)ν λ μ¬μ©ν©λλ€.
μ¦μλ‘λ©μ μ§μ°λ‘λ©μ λ°λκ°λ μΌλ‘ μ€μ μν°ν°λ₯Ό μ¬μ©νμ§ μλ κ²½μ°μλ μν°ν°λ₯Ό μ‘°νν λ μ°κ΄λ μν°ν°λ₯Ό ν¨κ» μ‘°ννλ λ°©μμ λλ€.
μ¦μλ‘λ© μ¬μ© : λ€μ€μ± μ΄λ Έν μ΄μ μ fetch μμ±μ FetchType.EAGERλ‘ μ€μ
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
μ€μ μ€νλλ SQL
select
M.*,
T.*
from Member M
left outer join
Team T on
M.TEAM_ID = T.TEAM_ID
where M.MEMBER_ID = 1L
- μ¦μ λ‘λ©μ κ²½μ° μ¦μ λ‘λ©λλ μν°ν°λ€μ Joinνλ©° νλ²μ 쿼리λ₯Ό μ€νν©λλ€.
- μ¦μ λ‘λ©μ κ²½μ° κΈ°λ³Έμ μΌλ‘ λ΄λΆ μ‘°μΈ(쑰건μ λ§λ λ°μ΄ν°λ§ μΆμΆ κ΅μ§ν©)μ΄ μλ μΈλΆ μ‘°μΈ(쑰건μ λ§λ λ°μ΄ν°λ , 쑰건μ λ§μ§ μλλ°μ΄ν°λ ν©μ§ν©μΌλ‘ μΆμΆ)ν©λλ€.
- 쑰건μ λ§μ§ μλ κ²½μ° null κ°μΌλ‘ μ²λ¦¬.
- λ΄λΆ μ‘°μΈμ΄ μΈλΆ μ‘°μΈλ³΄λ€ μ±λ₯μ΄λ μ΅μ νλ©΄μμ μ 리νκΈ° λλ¬Έμ λ΄λΆ μ‘°μΈμ μ¬μ©νλ κ²μ΄ μ’μ΅λλ€. μ΄λ»κ²?
- @JoinColumnμ nullable μμ±μ falseλ‘ μ€μ νλ©΄ λ΄λΆμ‘°μΈμ μ¬μ©ν©λλ€.
μ§μ°λ‘λ© μ¬μ© : λ€μ€μ± μ΄λ Έν μ΄μ μ fetch μμ±μ FetcchType.LAZYλ‘ μ€μ
@Entity
public class Member {
...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
Member member = em.find(Member.class, 1L);
- Teamμ μ§μ° λ‘λ©μ μ¬μ©νκΈ° λλ¬Έμ memberλ§ μ‘°ννκ³ νμ μ‘°ννμ§ μμ΅λλ€.
- λμ team λ©€λ²λ³μμ νλ‘μ κ°μ²΄λ₯Ό λ£μ΄ λ‘λλ€!
Team team = member.getTeam(); // νλ‘μ κ°μ²΄!
- teamμ νλ‘μ κ°μ²΄!
team.getName(); // νλ‘μ κ°μ²΄ μ΄κΈ°ν νΈμΆν΄μ μ€μ ν κ°μ²΄μ λ©μλ νΈμΆ
- μ΄λ κ² μ€μ νΈμΆμ΄ μΌμ΄λλ κ²½μ°μλ§ νλ‘μ κ°μ²΄λ₯Ό ν΅ν΄ μ€μ κ°μ²΄λ₯Ό μ¬μ©ν΄ λ‘λ©ν©λλ€.
- νλ‘μ κ°μ²΄ μ΄κΈ°ν κ³Όμ μμ select * from team where team_id = ? SQLμ΄ μ€νλ©λλ€.
+μ§μ°λ‘λ©, μ¦μλ‘λ© κΈ°λ³Έ μ λ΅
μ°κ΄λ μν°ν°κ° νλμΈ κ²½μ° ( @ManyToOne, @OneToOne ) : μ¦μλ‘λ© μ¬μ©
μ°κ΄λ μν°ν°κ° μ¬λ¬κ°μΈ κ²½μ° ( @ManyToMany, @OneToMany ) : μ§μ°λ‘λ© μ¬μ©
μ°κ΄λμ΄ μλ μν°ν°κ° 컬λ μ μΈ κ²½μ° μ±λ₯μ ν μ΄μλ‘ μ¦μ λ‘λ©μ κΆμ₯λμ§ μμ΅λλ€.
+ 컬λ μ μ νλ‘μ κ°μ²΄κ° μλ '컬λ μ λνΌ'λΌλ νλ‘μκ° μ§μ°λ‘λ©μ μ²λ¦¬ν΄μ€λλ€.
<μ 리>
- μ§μ°λ‘λ© : μ°κ΄λ μν°ν°λ₯Ό νλ‘μλ‘ μ‘°ν. νλ‘μλ₯Ό μ€μ μ¬μ©ν λ μ΄κΈ°ννλ©΄μ λ°μ΄ν°λ² μ΄μ€λ₯Ό μ‘°ν
- μ¦μλ‘λ© : μ°κ΄λ μν°ν°λ₯Ό μ¦μ μ‘°ν. (SQL JOIN μ¬μ©)
- μ¦μλ‘λ©μ κΈ°λ³Έμ μΌλ‘ νμ μΈλΆ μ‘°μΈμ μ¬μ©νκΈ° λλ¬Έμ μ±λ₯ μ΅μ νλ₯Ό μν λ΄λΆ μ‘°μΈμ μ¬μ©νκ³ μΆμΌλ©΄ λ€μ€μ± μ΄λ Έν μ΄μ μ nullable μμ±μ falseλ‘ μ§μ νλ©΄ λ©λλ€.
<μ°Έκ³ μλ£>