๐ JPA ๋จ๋ฐฉํฅ ๋งคํ๊ณผ ์๋ฐฉํฅ ๋งคํ
์ ๋จ๋ฐฉํฅ ๋งคํ์ ์ฐ์ ์ ์ผ๋ก ์ฌ์ฉํ๋๊ฐ?
- ๋จ์์ฑ๊ณผ ์ ์ง๋ณด์์ฑ
- ๋จ๋ฐฉํฅ ๋งคํ์ ์ฝ๋๋ฅผ ๋จ์ํ๊ฒ ๋ง๋ค๊ณ ์ ์ง๋ณด์๋ฅผ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
- ์๋ฐฉํฅ ๋งคํ์ ๊ฐ์ฒด ๊ฐ์ ๊ด๊ณ๋ฅผ ์์ชฝ์์ ๊ด๋ฆฌํด์ผ ํ๋ฏ๋ก ๋ณต์ก๋๊ฐ ์ฆ๊ฐํฉ๋๋ค.
- ์ฑ๋ฅ
- ๋จ๋ฐฉํฅ ๋งคํ์ ํ์ ์๋ ์ถ๊ฐ ์ฟผ๋ฆฌ ์์ฑ์ ๋ฐฉ์งํฉ๋๋ค.
- ์๋ฐฉํฅ ๋งคํ์ ์๋ชป ์ฌ์ฉํ ๊ฒฝ์ฐ ์์์น ๋ชปํ
SELECT
๋UPDATE
์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
- ์ฐ๊ด๊ด๊ณ์ ๋ณต์ก์ฑ
- ์๋ฐฉํฅ ๋งคํ์ ๋ ๊ฐ์ฒด ๊ฐ์ ๊ด๊ณ๋ฅผ ์์ชฝ์์ ๋๊ธฐํํด์ผ ํ๋ฏ๋ก ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ์๋ชป ์์ฑํ๊ฑฐ๋ ๋๋ฝํ๋ฉด ๋ฐ์ดํฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
๋จ๋ฐฉํฅ ๋งคํ๊ณผ ์๋ฐฉํฅ ๋งคํ์ ์ฐจ์ด
1. ๋จ๋ฐฉํฅ ๋งคํ (์ถ์ฒ)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team; // Member → Team ๋จ๋ฐฉํฅ
}
Member
์ํฐํฐ์์๋งTeam
์ ์ฐธ์กฐํฉ๋๋ค.- ๊ฐ๋จํ๊ณ , ๋ฐ์ดํฐ ํ๋ฆ์ด ๋ช ํํฉ๋๋ค.
Team
์์Member
๋ฅผ ์ฐธ์กฐํ์ง ์์๋ ๋๋ถ๋ถ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
2. ์๋ฐฉํฅ ๋งคํ
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team; // Member → Team
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>(); // Team → Member
}
- ์์ชฝ์์ ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
Member
์Team
๊ฐ์ ์ฐ๊ด๊ด๊ณ๋ฅผ ๋๊ธฐํํด์ผ ํฉ๋๋ค.- ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ์๋ชป ์์ฑํ๊ฑฐ๋ ํธ์ถํ์ง ์์ผ๋ฉด ๋ฐ์ดํฐ ๋ถ์ผ์น๊ฐ ๋ฐ์ํฉ๋๋ค.
์๋ฐฉํฅ ๋งคํ์ ๋ฌธ์ ์ ์์
1. ์์์น ๋ชปํ ์ถ๊ฐ ์ฟผ๋ฆฌ ๋ฐ์
Team team = em.find(Team.class, 1L);
List<Member> members = team.getMembers();
for (Member member : members) {
System.out.println(member.getName());
}
- ๋ฌธ์ :
team.getMembers()
๋ฅผ ํธ์ถํ๋ฉด, JPA๋ ์ถ๊ฐ ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ฌMember
๋ฅผ ๊ฐ์ ธ์ต๋๋ค. - ์๋ฐฉํฅ ๋งคํ์ด ํ์ ์๋ ๊ฒฝ์ฐ์๋ ๋ถํ์ํ ์ฟผ๋ฆฌ๋ฅผ ์คํํ ์ ์์ต๋๋ค.
2. ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋ ๋๋ฝ์ผ๋ก ์ธํ ๋ฐ์ดํฐ ๋ถ์ผ์น
Team team = new Team();
team.setName("teamA");
Member member = new Member();
member.setName("member1");
member.setTeam(team); // Team์ ์ถ๊ฐ๋์ง ์์
em.persist(team);
em.persist(member);
- ๋ฌธ์ :
team.getMembers()
๋ฅผ ํธ์ถํ๋ฉดmember
๊ฐ ๋ฆฌ์คํธ์ ํฌํจ๋์ง ์์ต๋๋ค. - ์๋ฐฉํฅ ๋งคํ์์๋ ๋ฐ๋์ ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋(
team.addMember(member)
)๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
๋จ๋ฐฉํฅ ๋งคํ์ผ๋ก ํด๊ฒฐ ๊ฐ๋ฅ
1. ๋จ๋ฐฉํฅ ๋งคํ์ผ๋ก ์ถฉ๋ถํ ์
Member member = new Member();
member.setName("member1");
member.setTeam(team); // ๋จ๋ฐฉํฅ ์ค์
em.persist(member);
- ๊ฐ๋จํ ๋งคํ ๊ตฌ์กฐ๋ก ๋น์ฆ๋์ค ๋ก์ง์ด ๋ช ํํด์ง.
- ๋ถํ์ํ ์ฟผ๋ฆฌ์ ๋ฐ์ดํฐ ๋ถ์ผ์น ๋ฌธ์ ๋ฅผ ๋ฐฉ์ง.
2. ์๋ฐฉํฅ ๋งคํ์ด ํ์ํ ๊ฒฝ์ฐ
Team team = em.find(Team.class, 1L);
List<Member> members = team.getMembers(); // ํ์ํ ๋๋ง ์ฌ์ฉ
๊ฒฐ๋ก
- ๋จ๋ฐฉํฅ ๋งคํ์ ์ค๊ณ์ ์ ์ง๋ณด์์ ๋ณต์ก์ฑ์ ์ค์ด๊ณ , ์๊ธฐ์น ์์ ๋ฌธ์ ๋ฅผ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
- ํ์ํ ๋๋ง ์๋ฐฉํฅ ๋งคํ์ ์ฌ์ฉํ๊ณ , ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ํตํด ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ๋ณด์ฅํด์ผ ํฉ๋๋ค.
- ์๋ฐฉํฅ ๋งคํ์ ๊ฐ๋ ฅํ์ง๋ง, ์ ์ ํ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ ์ง๋ณด์์ ์ฑ๋ฅ์ ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค.
๐ ์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ ์ค์ ํ๋ค๊ฐ ์ผ์ด๋ ์ ์๋ ๋ถ์์ฉ
1. ๋ฌดํ ๋ฃจํ ๋ฌธ์
์์ธ:
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ์์ toString()
, hashCode()
, ๋๋ equals()
๋ฉ์๋์์ ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ์ฌ๊ท์ ์ผ๋ก ํธ์ถํ๋ ๊ฒฝ์ฐ ๋ฌดํ ๋ฃจํ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์์:
@Override
public String toString() {
return "Member{" +
"id=" + id +
", team=" + team + // team.toString() ํธ์ถ
'}';
}
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ
toString()
์์ ์ ์ธํฉ๋๋ค. - ๋ผ์ด๋ธ๋ฌ๋ฆฌ(Jackson, Lombok ๋ฑ)๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
@JsonIgnore
๋๋@ToString.Exclude
๋ฅผ ์ ์ฉํฉ๋๋ค.
2. ๋ฐ์ดํฐ ๋ถ์ผ์น ๋ฌธ์
์์ธ:
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ๊ณผ ๋น์ฃผ์ธ์ ์ ๋๋ก ๊ด๋ฆฌํ์ง ์์ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ์ผ๊ด๋์ง ์์ ์ ์์ต๋๋ค.
์์:
Member member = new Member();
Team team = new Team();
member.setTeam(team); // ์ฃผ์ธ ์ค์
team.getMembers().add(member); // ๋น์ฃผ์ธ ์ค์
em.persist(member);
em.persist(team);
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ๋ง ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๋๋ก ์ฝ๋๋ฅผ ์์ฑํฉ๋๋ค.
- ์ฐ๊ด๊ด๊ณ๋ฅผ ์ค์ ํ ๋ ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
public void addMember(Member member) {
this.members.add(member);
member.setTeam(this); // ์ฃผ์ธ ํ๋ ์ค์
}
3. N+1 ๋ฌธ์
์์ธ:
์ฐ๊ด๊ด๊ณ๊ฐ ์ฆ์ ๋ก๋ฉ(FetchType.EAGER
)์ผ๋ก ์ค์ ๋์ด ์์ ๋, ํ ์ฟผ๋ฆฌ์์ ๋ค์์ ์ํฐํฐ๋ฅผ ์กฐํํ๋ฉด ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํด ์ถ๊ฐ์ ์ธ ์ฟผ๋ฆฌ(N๊ฐ)๊ฐ ์คํ๋ฉ๋๋ค.
์์:
List teams = em.createQuery("SELECT t FROM Team t", Team.class).getResultList();
for (Team team : teams) {
System.out.println(team.getMembers().size()); // ๊ฐ ํ์ ๋ฉค๋ฒ ์กฐํ
}
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ์ง์ฐ ๋ก๋ฉ(Lazy Loading)์ผ๋ก ๋ณ๊ฒฝ:
@OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
private List members;
- ๋๋ ํ์น ์กฐ์ธ(Fetch Join)์ ์ฌ์ฉ:
List teams = em.createQuery(
"SELECT t FROM Team t JOIN FETCH t.members", Team.class).getResultList();
4. ์ค๊ณ ๋ณต์ก๋ ์ฆ๊ฐ
์์ธ:
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ ๋จ๋ฐฉํฅ๋ณด๋ค ์ค๊ณ์ ๊ตฌํ์ด ๋ณต์กํ๋ฉฐ, ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ๊ด๋ฆฌํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฝ๋๊ฐ ๋ณต์กํด์ง ์ ์์ต๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ์๋ฐฉํฅ์ด ๊ผญ ํ์ํ์ง ๊ฒํ :
- ํ์ชฝ ๋ฐฉํฅ์ผ๋ก๋ง ๋ฐ์ดํฐ๋ฅผ ํ์ํด๋ ์ถฉ๋ถํ๋ค๋ฉด ๋จ๋ฐฉํฅ ๊ด๊ณ๋ก ์ค๊ณํฉ๋๋ค.
5. ์ฑ๋ฅ ๋ฌธ์
์์ธ:
์๋ฐฉํฅ ๊ด๊ณ๋ก ์ธํด ํ์ํ์ง ์์ ๋ฐ์ดํฐ๋ฅผ ๋ถํ์ํ๊ฒ ๋ก๋ฉํ๊ฑฐ๋ ์กฐ์ธ ์ฟผ๋ฆฌ๊ฐ ๊ณผ๋ํ๊ฒ ๋ณต์กํด์ง ์ ์์ต๋๋ค.
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- JPQL๋ก ํ์ํ ๋ฐ์ดํฐ๋ง ์กฐํํ๊ฑฐ๋, ์ฐ๊ด๊ด๊ณ๋ฅผ ๋จ๋ฐฉํฅ์ผ๋ก ์ค์ฌ์ ๋ณต์ก์ฑ์ ๋ฎ์ถฅ๋๋ค.
- ๋ณต์กํ ์ฐ๊ด๊ด๊ณ์ ๊ฒฝ์ฐ DTO๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ฐ์ดํฐ๋ง ๊ฐ์ ธ์ต๋๋ค.
6. ์ญ์ ์ ์ฝ ๋ฌธ์ (Cascade ๊ด๋ จ)
์์ธ:
์๋ฐฉํฅ ๊ด๊ณ์์ ์ญ์ ์ ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ํจ๊ป ์ญ์ ํ๋ ค๋ฉด CascadeType.ALL
์ ์ค์ ํด์ผ ํฉ๋๋ค. ์๋ชป ์ค์ ํ๋ฉด ์๋์น ์๊ฒ ๊ด๋ จ ์ํฐํฐ๊ฐ ์ญ์ ๋ ์ ์์ต๋๋ค.
์์:
team.getMembers().remove(member);
ํด๊ฒฐ ๋ฐฉ๋ฒ:
CascadeType
์ ์ ์คํ ์ฌ์ฉํ๊ณ , ์ญ์ ๋ ๋ช ์์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
7. ํ๋ก์ ์ด๊ธฐํ ๋ฌธ์
์์ธ:
์ง์ฐ ๋ก๋ฉ(FetchType.LAZY
)์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ, ์ฐ๊ด๋ ์ํฐํฐ๋ฅผ ์ด๊ธฐํํ๊ธฐ ์ ์ ์ธ์
์ด ์ข
๋ฃ๋๋ฉด LazyInitializationException
์ด ๋ฐ์ํฉ๋๋ค.
์์:
@Transactional
public void someMethod() {
Team team = em.find(Team.class, 1L);
em.close(); // ์ธ์
์ข
๋ฃ
team.getMembers(); // LazyInitializationException ๋ฐ์
}
ํด๊ฒฐ ๋ฐฉ๋ฒ:
- ์ฐ๊ด๋ ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๋ก๋ํ๊ธฐ ์ํด ํ์น ์กฐ์ธ์ ์ฌ์ฉํ๊ฑฐ๋, ์ํฐํฐ๋ฅผ ์ฌ์ฉํ ์์ ์ ์ธ์ ์ด ์ด๋ ค ์์ด์ผ ํฉ๋๋ค.
์ ๋ฆฌ
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ํ ์ ์๋ ์ฃผ์ ๋ถ์์ฉ๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
๋ฌธ์ ์ | ์์ธ | ํด๊ฒฐ ๋ฐฉ๋ฒ |
---|---|---|
๋ฌดํ ๋ฃจํ | toString() , equals() , hashCode() ์ฌ์ฉ |
์ฐ๊ด ๊ฐ์ฒด ์ ์ธ, @JsonIgnore ๋ฑ ์ ์ฉ |
๋ฐ์ดํฐ ๋ถ์ผ์น | ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ ์ ๋๋ก ๊ด๋ฆฌํ์ง ์์ | ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ๋ง ์์ , ํธ์ ๋ฉ์๋ ์ฌ์ฉ |
N+1 ๋ฌธ์ | ์ฆ์ ๋ก๋ฉ ์ฌ์ฉ | ์ง์ฐ ๋ก๋ฉ ์ค์ , JPQL ํ์น ์กฐ์ธ ์ฌ์ฉ |
์ค๊ณ ๋ณต์ก๋ ์ฆ๊ฐ | ์๋ฐฉํฅ ๊ด๊ณ๋ฅผ ๋จ๋ฐ | ๋จ๋ฐฉํฅ ๊ด๊ณ๋ก ๋์ฒด |
์ฑ๋ฅ ๋ฌธ์ | ๋ถํ์ํ ๋ฐ์ดํฐ ๋ก๋ฉ, ๋ณต์กํ ์กฐ์ธ | JPQL ๋๋ DTO ์ฌ์ฉ |
์ญ์ ์ ์ฝ ๋ฌธ์ | ์๋ชป๋ CascadeType ์ค์ |
Cascade ์ค์ ์ต์ํ |
ํ๋ก์ ์ด๊ธฐํ ๋ฌธ์ | ์ง์ฐ ๋ก๋ฉ ๋ฐ์ดํฐ ์ ๊ทผ ์ ์ธ์ ์ข ๋ฃ | ์ธ์ ์ ์ง, ํ์น ์กฐ์ธ ์ฌ์ฉ |
'JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
JPA ํ์ต๋ชฉ๋ก (2) | 2024.12.21 |
---|---|
JPA ์ํฐํฐ ์๋ฐฉํฅ ๊ด๊ณ์์ ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ด ํ์ํ ์ด์ (1) | 2024.12.09 |
JPA ์ํฐํฐ์ ๊ฐ์ฒด์งํฅ์ ์ค๊ณ (0) | 2024.12.09 |
JPA PK ์์ฑ์ ๋ต @GeneratedValue(strategy = GenerationType.โ) (0) | 2024.12.09 |
JPA EntityManagerFactory, EntityManager๋ ์ฌ์ฉ ํ ๊ผญ ๋ซ์์ผ ํ๋ค (3) | 2024.12.05 |
๋๊ธ