πŸƒJPA 연관관계 λ§€ν•‘

Spring JPA

2 minute read

μ—”ν‹°ν‹° λ§€ν•‘

  • @Entityκ°€ 뢙은 ν΄λž˜μŠ€λŠ” JPAκ°€ κ΄€λ¦¬ν•˜λ©° 엔티티라고 λΆ€λ₯Έλ‹€.
  • RDBMS의 Tableκ³Ό λ§€ν•‘λœλ‹€.

μ£Όμ˜μ‚¬ν•­

  • νŒŒλΌλ©”ν„°κ°€ μ—†λŠ” public λ˜λŠ” protected κΈ°λ³Έ μƒμ„±μžκ°€ ν•„μš”ν•˜λ‹€.
  • final 클래슀, enum, interface, inner ν΄λž˜μŠ€μ—λŠ” μ‚¬μš©ν•  수 μ—†λ‹€.
  • μ €μž₯ν•  ν•„λ“œμ— final을 μ‚¬μš©ν•  수 μ—†λ‹€.
  • λ‚΄λΆ€ κ΅¬ν˜„μƒ μƒμ†λ°›λŠ” ν”„λ‘μ‹œ 객체가 μƒκ²¨μ„œ λ°œμƒν•˜λŠ” μ£Όμ˜μ‚¬ν•­λ“€μ΄λ‹€.

λ°μ΄ν„°λ² μ΄μŠ€ μŠ€ν‚€λ§ˆ μžλ™ 생성

create : κΈ°μ‘΄ ν…Œμ΄λΈ” μ‚­μ œ ν›„ λ‹€μ‹œ 생성(DROP + CREATE) ν•œλ‹€.
create-drop : create와 κ°™μœΌλ‚˜ μ’…λ£Œ μ‹œμ μ— ν…Œμ΄λΈ”μ„ dropν•œλ‹€
update : λ³€κ²½λœ μ‚¬ν•­λ§Œ λ°˜μ˜λœλ‹€.
validate : 엔티티와 ν…Œμ΄λΈ”μ΄ 정상 λ§€ν•‘λ˜μ—ˆλŠ”μ§€ ν™•μΈλ§Œ ν•œλ‹€.
none : μ‚¬μš©ν•˜μ§€ μ•ŠμŒ

  • 운영 ν™˜κ²½μ—μ„œλŠ” μ ˆλŒ€ create, create-drop, updateλ₯Ό μ‚¬μš©ν•΄μ„œλŠ” μ•ˆλœλ‹€.
  • 개발 μ΄ˆκΈ°λ‹¨κ³„λŠ” create λ˜λŠ” update, ν…ŒμŠ€νŠΈμ„œλ²„λŠ” update λ˜λŠ” validate, μŠ€ν…Œμ΄μ§•κ³Ό 운영 μ„œλ²„λŠ” validate λ˜λŠ” none이 ꢌμž₯λœλ‹€.

μž„λ² λ””λ“œ νƒ€μž…

  • @Embeddable : κ°’ νƒ€μž…μ„ μ •μ˜ν•˜λŠ” 곳에 ν‘œμ‹œ
  • @Embedded : κ°’ νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” 곳에 ν‘œμ‹œ
  • κΈ°λ³Έ μƒμ„±μžκ°€ ν•„μˆ˜μ΄λ‹€.
  • λ³„λ„μ˜ 클래슀둜 μ •μ˜ν•  수 μžˆμ§€λ§Œ ν…Œμ΄λΈ” μƒμœΌλ‘œλŠ” μ‚¬μš©ν•˜λŠ” κ³³κ³Ό 같은 ν…Œμ΄λΈ”μ— μœ„μΉ˜ν•œλ‹€.
  • 잘 μ„€κ³„ν•œ ORM μ–΄ν”Œλ¦¬μΌ€μ΄μ…˜μ€ λ§€ν•‘ν•œ ν…Œμ΄λΈ”μ˜ μˆ˜λ³΄λ‹€ 클래슀의 μˆ˜κ°€ 더 λ§Žλ‹€.
  • λ‹€λ§Œ μž„λ² λ””λ“œ νƒ€μž…μœΌλ‘œ μ •μ˜ν•œ κ°μ²΄λŠ” κ°μ²΄μ΄λ―€λ‘œ λŒ€μž…μ‹œ 볡사가 μ•„λ‹ˆλΌ μ°Έμ‘°κ°€ μΌμ–΄λ‚œλ‹€. 곡유 참쑰의 λ¬Έμ œκ°€ μžˆμ„ 수 μžˆμœΌλ―€λ‘œ μ£Όμ˜ν•œλ‹€. λΆˆλ³€κ°μ²΄λ‘œ λ§Œλ“ λ‹€λ©΄ κ°€μž₯ μ’‹λ‹€.
  • μž„λ² λ””λ“œ νƒ€μž…μ²˜λŸΌ κ°’νƒ€μž…μΈ 경우 μ»¬λ ‰μ…˜μœΌλ‘œ μ—¬λŸ¬κ°œλ₯Ό μ €μž₯ν•  λ•Œ λ³„λ„μ˜ ν…Œμ΄λΈ”μ΄ ν•„μš”ν•˜λ‹€. 그리고 κ°’ νƒ€μž…μ€ 좔적이 μ–΄λ ΅λ‹€. μ»¬λ ‰μ…˜μœΌλ‘œ μ‚¬μš©ν•΄μ•Όν•  경우 μΌλŒ€λ‹€ 관계λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 ꢌμž₯λœλ‹€.
  • μ‹λ³„μžκ°€ ν•„μš”ν•˜κ³ , μ§€μ†ν•΄μ„œ 값을 μΆ”μ ν•˜κ³  λ³€κ²½ν•΄μ•Ό ν•œλ‹€λ©΄ 그것은 κ°’ νƒ€μž…μ΄ μ•„λ‹ˆλΌ 엔티티이닀.

μ˜μ†μ„± 전이

  • μ—”ν‹°ν‹°λ₯Ό μ˜μ†ν™” ν•  λ•Œ μ—°κ΄€λœ 엔티티도 ν•¨κ»˜ μ˜μ†ν™”ν•˜λŠ” κΈ°λŠ₯을 μ œκ³΅ν•œλ‹€.
  • 예λ₯Ό λ“€μ–΄ 멀버λ₯Ό μ €μž₯ν• λ•Œ νŒ€λ„ ν•¨κ»˜ μ €μž₯, μ‚­μ œ
  • 연관관계 λ§€ν•‘κ³ΌλŠ” μ „ν˜€ 관계가 μ—†λ‹€! 주의. (이건 μ…€λ ‰νŠΈμ™€ κ΄€κ±”λœκ±°)
@OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)  

μ’…λ₯˜

ALL : λͺ¨λ‘ 적용
PERSIST : μ˜μ†
REMOVE : μ‚­μ œ
MERGE : 병합
REFRESH
DETACH

연관관계 λ§€ν•‘

단방ν–₯

@Entity  
public class Member {  
	@ManyToOne  
	@JoinColumn(name = "TEAM_ID")  
	private Team team;  
}  
  • μœ„μ™€ 같이 @JoinColumn을 톡해 연관관계λ₯Ό λ§€ν•‘ν•˜λŠ” 경우, MEMBER ν…Œμ΄λΈ”μ— TEAM_IDλΌλŠ” ForeignKeyκ°€ μΆ”κ°€λ˜λŠ” ν˜•νƒœκ°€ λœλ‹€.

ν”„λ‘μ‹œ 객체

  • ν•˜μ§€λ§Œ team이 μ‹€μ œλ‘œ μ½”λ“œμƒμ—μ„œ 참쑰되기 μ „κΉŒμ§€λŠ” team은 ν”„λ‘μ‹œ 객체의 ν˜•νƒœλ‘œ μ‘΄μž¬ν•˜κ²Œ 되고 이λ₯Ό 톡해 μ§€μ—° λ‘œλ”©μ΄ κ°€λŠ₯ν•˜κ²Œ λœλ‹€.
  • μœ„μ˜ κ²½μš°μ—λ„ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ°ΎλŠ” μ—”ν‹°ν‹°κ°€ 이미 μžˆλ‹€λ©΄ ν”„λ‘₯μ‹œκ°€ μ•„λ‹Œ μ‹€μ œ μ—”ν‹°ν‹°κ°€ λ°˜ν™˜λœλ‹€.
  • @ManyToOne(fetch = FetchType.EAGER)둜 μ„€μ •ν•  경우(사싀 기본값이닀) μ§€μ—°λ‘œλ”©μ΄ μΌμ–΄λ‚˜μ§€ μ•Šκ³  μ¦‰μ‹œ λ‘œλ”©μ΄ μΌμ–΄λ‚œλ‹€. ν•˜μ§€λ§Œ μ ˆλŒ€λ‘œ EAGERλ₯Ό μ‚¬μš©ν•˜μ§€ 말자.
  • μ¦‰μ‹œλ‘œλ”©μ€ N+1 문제λ₯Ό μΌμœΌν‚¨λ‹€. μƒμƒλ§Œ 해봐도 μ—„μ²­ 큰 트리 ν˜•νƒœμ˜ 객체λ₯Ό κ°€μ Έμ˜€κΈ° μœ„ν•΄ μ—„μ²­λ‚œ λΆ€ν•˜κ°€ λ°œμƒν•˜λŠ” 일이 μžˆμ„ 수 μžˆλ‹€. 기본값이 EAGERμ΄λ―€λ‘œ μ—΄μ‹¬νžˆ LAZY둜 μ„€μ •ν•˜μž.
  • μ¦‰μ‹œλ‘œλ”©μ΄ ν•„μš”ν•˜λ‹€λ©΄? EAGERκ°€ μ•„λ‹ˆλΌ FETCH JOIN을 μ΄μš©ν•˜μž! N+1 λ¬Έμ œκ°€ ν•΄κ²°λœλ‹€.

μ–‘λ°©ν–₯

@Entity  
public class Member {  
	@ManyToOne  
	@JoinColumn(name = "TEAM_ID")	// μ™Έλž˜ν‚€λ₯Ό λ§€ν•‘ν•  λ•Œ μ‚¬μš©ν•œλ‹€. 즉 λ©€λ²„μ˜ ν…Œμ΄λΈ”μ— μ™Έλž˜ν‚€  
	private Team team;  
}  
  
@Entity  
public class Team {  
	@OneToMany(mappedBy = "team")	// λ°˜λŒ€μͺ½μ— λ§€ν•‘λ˜λŠ” ν•„λ“œμ˜ 값을 μ€€λ‹€  
	List<Member> members = new ArrayList<>();  
}  
  • ν…Œμ΄λΈ”μ€ μ™Έλž˜ν‚€ ν•˜λ‚˜λ‘œ 두 ν…Œμ΄λΈ”μ˜ μ—°κ΄€ 관계λ₯Ό κ΄€λ¦¬ν•œλ‹€.
  • 반면 κ°μ²΄λŠ” 각각의 μ°Έμ‘°(team, members)λ₯Ό 각각 κ°€μ§„λ‹€.
  • μ—°κ΄€ κ΄€κ³„μ˜ 주인이 μ™Έλž˜ ν‚€λ₯Ό κ°€μ§„λ‹€.
  • 주인이 μ•„λ‹Œμͺ½μ€ 읽기 만이 κ°€λŠ₯ν•˜λ‹€.
  • 단방ν–₯ λ§€ν•‘λ§ŒμœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€ μž…μž₯μ—μ„œλŠ” 이미 연관관계가 λ§€ν•‘λ˜μ§€λ§Œ μ–‘λ°©ν–₯을 톡해 λ°˜λŒ€ λ°©ν–₯으둜 객체 κ·Έλž˜ν”„ 탐색기λŠ₯이 μΆ”κ°€λ˜λŠ” 것이닀.
  • 주인이 μ•„λ‹Œ 경우 mappedBy 속성을 톡해 주인을 μ§€μ •ν•œλ‹€.
  • μΌλŒ€λ‹€λŠ” 직관적이지 μ•Šλ‹€. λ‹€λŒ€μΌμ„ μ‚¬μš©ν•˜λ„λ‘ ν•˜κ³  λ‹€μͺ½μ— μ™Έλž˜ν‚€λ₯Ό 두도둝 ν•œλ‹€.

연관관계 편의 λ©”μ†Œλ“œ

Member member = new Member();  
Team team = new Team;  
member.setTeam(team);  
team.addMember(member);  
  
// μ–‘λ°©ν–₯의 경우 연관관계 편의 λ©”μ†Œλ“œ ν™œμš©μ„ 톡해 null μ°Έμ‘°λ₯Ό ν”Όν•˜μž   
public void setTeam(Team team) {  
	this.team = team;  
	team.addMember(this);  
}  
  • μ–‘λ°©ν–₯ λ§€ν•‘μ‹œ toString, lombok, JSON 라이브러리 등을 ν†΅ν•œ λ¬΄ν•œλ£¨ν”„λ„ 쑰심해야 ν•œλ‹€.