์ฐ๊ด๊ด๊ณ๋ฅผ ๋งคํํ ๋๋ ๋ค์์ 3๊ฐ์ง๋ฅผ ๊ณ ๋ คํด์ ๋งคํํ์ฌ์ผ ํฉ๋๋ค.
- ๋ค์ค์ฑ (์ผ๋ ๋ค, ๋ค๋ ์ผ, ๋ค๋ ๋ค, ์ผ๋ ์ผ)
- ๋ฐฉํฅ (์๋ฐฉํฅ, ๋จ๋ฐฉํฅ)
- ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
๐ง ๋ค์ค์ฑ
์ฐ๊ด๊ด๊ณ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ค์ค์ฑ์ด ์์ต๋๋ค.
๋ค๋์ผ(N : 1)[ManyToOne]
์ผ๋๋ค(1 : N)[OneToMany]
์ผ๋์ผ(1 : 1)[OneToOne]
๋ค๋๋ค(N : N)[ManyToMany]
๐ง ๋ฐฉํฅ
๋ฐฉํฅ์๋ ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ์ด ์์ต๋๋ค.
DB ํ ์ด๋ธ์ ์ธ๋ ํค(Foreign key)ํ๋๋ก ์กฐ์ธ(Join)์ ์ฌ์ฉํด์ ์๋ฐฉํฅ์ผ๋ก ์ฟผ๋ฆฌ๊ฐ ๊ฐ๋ฅํฉ๋๋ค.
๋ฐ๋ผ์ DB์๋ ๋ฐฉํฅ์ ๊ฐ๋ ์ด ์์ต๋๋ค.
๊ทธ๋ฌ๋ ๊ฐ์ฒด์ ๊ฒฝ์ฐ, ์ฐธ์กฐ์ฉ ํ๋๋ฅผ ๊ฐ์ง๊ณ ์๋ ๊ฐ์ฒด๋ง ์ฐ๊ด๋ ๊ฐ์ฒด๋ฅผ ์กฐํํ ์ ์์ผ๋ฏ๋ก ๋ฐฉํฅ์ ๊ฐ๋ ์ด ์กด์ฌํฉ๋๋ค.
๊ฐ์ฒด ๊ด๊ณ์์ ํ ์ชฝ๋ง ๋ฐ๋์ชฝ์ ์ฐธ์กฐํ๋ ๊ด๊ณ๋ฅผ ๋จ๋ฐฉํฅ, ์ ์ชฝ ๋ชจ๋ ์๋ก๋ฅผ ์ฐธ์กฐํ๋ ๊ด๊ณ๋ฅผ ์๋ฐฉํฅ์ด๋ผ๊ณ ํฉ๋๋ค.
๐ง ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ ์ธ๋ ํค(FK) ํ๋๋ก ๋ ํ ์ด๋ธ์ด ์ฐ๊ด๊ด๊ณ๋ฅผ ๋งบ์ต๋๋ค.
์ฆ ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ด๋ฆฌํ๋ ํฌ์ธํธ๋ ์ธ๋ ํค ํ๋์ ๋๋ค.
๋ฐ๋ฉด ๊ฐ์ฒด์์๋ ์๋ฐฉํฅ ๊ด๊ณ๋ก ๋งคํํ๋ฉด A -> B, B -> A, ๋ ๊ณณ์์ ์๋ก๋ฅผ ์ฐธ์กฐํ๋ฏ๋ก ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ด๋ฆฌํ๋ ํฌ์ธํธ๋ ๋ ๊ณณ์ด ๋ฉ๋๋ค.
๋ฐ๋ผ์ JPA๋ ๋ ๊ฐ์ฒด ์ค ํ๋๋ฅผ ์ ํด์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋ง๋ค์ด์ผ ํ๋๋ฐ, ์ฌ๊ธฐ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๋ ๊ฐ์ฒด๋ฅผ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด๋ผ๊ณ ํฉ๋๋ค.
๋ณดํต ์ธ๋ ํค๋ฅผ ๊ฐ์ง ํ ์ด๋ธ๊ณผ ๋งคํ๋๋ ์ํฐํฐ๊ฐ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๋ ๊ฒ์ด ํจ์จ์ ์ด๋ฏ๋ก, ๋ณดํต ์ด๊ณณ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ผ๋ก ์ ํํฉ๋๋ค.
์ฃผ์ํ ์ ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๋ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ๋ง์ด ์ธ๋ ํค๋ฅผ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ, ์ฃผ์ธ์ด ์๋ ๊ณณ์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ๋๋ค.
๐ง ๋ ผ๋ฆฌ์ FK์ ๋ํ์ฌ
์ด๋ฒ ๊ธ์ ๋ฌผ๋ฆฌ์ ์ผ๋ก FK๊ฐ ์กด์ฌํ๋ ๊ฒฝ์ฐ์ ๋ํ ๋งคํ์ ๋ํด ์์ฑํ ๊ธ์ ๋๋ค.
๋ ผ๋ฆฌ์ FK๋ง ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๋ ๋ ผ๋ฆฌ์ FK๋ฅผ ์๋ฐ ํด๋์ค์ ํ๋๋ก ์ค์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
๊ฐ๋จํ๊ฒ ์ ์ฉํ ์ ์์ผ๋ฏ๋ก ๋์ด๊ฐ๊ฒ ์ง๋ง, ์ค์ ์ค๋ฌด์์๋ ๋ ผ๋ฆฌ์ FK๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค๊ณ ํฉ๋๋ค.
์ค์ Github์์๋ FK๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๊ณ ํ๋ฉฐ, ๋ค์์ ์ฐธ์กฐํ์๋ฉด ์ข์ ๊ฒ ๊ฐ์ต๋๋ค.
https://github.com/github/gh-ost/issues/331
์๋ฌดํผ ์ ํฌ๋ ํ์ต์ ์ํด ๋ฐฐ์ฐ๋ ๊ฒ์ด๊ธฐ์, ๊ณ์ํด์ ์ฐ๊ด๊ด๊ณ๋ฅผ ๋งคํํ๋ ๋ฒ์ ๋ํ์ฌ ๊ณต๋ถํ๋๋ก ํ๊ฒ ์ต๋๋ค.
๐ง ๋ค์ค์ฑ, ๋ฐฉํฅ, ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ๊ณ ๋ คํ ๋ชจ๋ ์ฐ๊ด๊ด๊ณ
- ๋ค๋์ผ : ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
- ์ผ๋๋ค : ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
- ์ผ๋์ผ : ์ฃผ ํ ์ด๋ธ ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
- ์ผ๋์ผ : ๋์ ํ ์ด๋ธ ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
- ๋ค๋๋ค : ๋จ๋ฐฉํฅ, ์๋ฐฉํฅ
์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ํธ๋ฅธ์์ผ๋ก ํํํ์์ต๋๋ค.
๐ง ๋ค๋์ผ [N : 1] - @ManyToOne
๐ณ ๋ค๋์ผ ๋จ๋ฐฉํฅ
์ฝ๋๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
...
}
ํ์์ Member.team์ผ๋ก ํ ์ํฐํฐ๋ฅผ ์ฐธ์กฐํ ์ ์์ง๋ง, ๋ฐ๋๋ก ํ์๋ ํ์์ ์ฐธ์กฐํ๋ ํ๋๊ฐ ์์ต๋๋ค.
๋ฐ๋ผ์ ํ์๊ณผ ํ์ ๋ค๋์ผ ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ์ ๋๋ค.
@ManyToOne
@JoinColumn(name = "TEAM_ID")
๋ค๋์ผ ์ฐ๊ด๊ด๊ณ์ด๋ฏ๋ก @ManyToOne์ ์ฌ์ฉํ์์ต๋๋ค.
@JoinColumn์ ์ฌ์ฉํ์ฌ Member.team ํ๋๋ฅผ TEAM_ID ์ธ๋ ํค์ ๋งคํํ์์ต๋๋ค.
๐ณ ์ธ๋ ํค๋ก ๋งคํํ ์ปฌ๋ผ ์ง์ ํ๋ ๋ฐฉ๋ฒ
referencedColumnName์ ํตํด ๋์ ํ ์ด๋ธ์ ์ด๋ ํ ์ปฌ๋ผ์ FK๋ก ์ฌ์ฉํ ์ง ์ง์ ํ ์ ์์ต๋๋ค.
๊ธฐ๋ณธ๊ฐ์ ์ฐธ์กฐํ๋ ํ ์ด๋ธ์ ๊ธฐ๋ณธํค(PK) ์ปฌ๋ผ๋ช ์ด๋ฏ๋ก ์๋์ผ๋ก ๊ธฐ๋ณธ ํค๊ฐ ์ธ๋ ํค๋ก ๋งคํ๋ฉ๋๋ค.
name ์์ฑ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ํ ์ด๋ธ์ FK๋ฅผ ์ ์ฅํ ์ปฌ๋ผ๋ช ์ ์ง์ ํ๋ ๊ฒ์ ๋๋ค.
์๋ฅผ ๋ค์ด referencedColumnName = "name"์ด๋ฉฐ, name = "fk_name"์ธ ๊ฒฝ์ฐ, ๋์ ํ ์ด๋ธ์ name ์ปฌ๋ผ์ ๊ฐ์ FK๋ก ์ฌ์ฉํ๋ฉฐ, ์ด๋ fk_name ์ปฌ๋ผ์ ์ ์ฅ๋ฉ๋๋ค.
๐ณ mappedBy๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด...
mappedBy๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ๋ค๋์ผ ๊ด๊ณ์ ๊ฒฝ์ฐ ์ค๊ฐ ํ ์ด๋ธ์ด ์์ฑ๋ฉ๋๋ค.
๋ค๋์ผ ๊ด๊ณ ์์
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
protected Member() {
}
public void setTeam(Team team) {
this.team = team;
}
}
@Entity
public class Team {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@OneToMany
private List<Member> members = new ArrayList<>();
}
๐ณ ๋ค๋์ผ ์๋ฐฉํฅ
์ฌ๊ธฐ์ ์ค์ํ ๊ฒ์ ๋จ๋ฐฉํฅ์ด ์๋ฐฉํฅ์ด ๋์๋ค๊ณ ํด์ ํ ์ด๋ธ์ ์ํฅ์ ์ฃผ๋๊ฒ์ด ์๋๋๋ค.
ํ ์ด๋ธ์ ๊ทธ๋๋ก์ด๊ณ , ๊ฐ์ฒด์์๋ง ๋ฐ๋๋ฐฉํฅ์ผ๋ก์ ์ฐ๊ด๊ด๊ณ๊ฐ ํ๋ ๋์ด๋ ๊ฒ์ ๋๋ค.
์ฝ๋๋ ๋ค์๊ณผ ๊ฐ์ด Team์๋ง ์ฐธ์กฐ๊ฐ ํ๋ ๋์ด๋ฉ๋๋ค.
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<>();
...
}
@OneToMany(mappedBy = "team")
์์ ์ด๋ ธํ ์ด์ ์ ํตํด, ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ, ์ฆ ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํ๋ ํ๋(Member์ team ํ๋)๋ฅผ ๋ช ์ํด ์ค ๊ฒ์ ๋๋ค.
๐ง ์ผ๋๋ค [1 : N] : @OneToMany
๐ณ ์ผ๋๋ค ๋จ๋ฐฉํฅ
์ผ๋๋ค ๋จ๋ฐฉํฅ ๊ด๊ณ๋ ํน์ดํ๊ฒ๋ Team ์ํฐํฐ์ Team.members๋ก MEMBER ํ ์ด๋ธ์ TEAM_ID ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
๋ณดํต์ ์์ ์ด ๋งคํํ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
๊ทธ๋ฌ๋ ์ผ๋๋ค(์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด 1) ๋จ๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ์์๋ ์ธ๋ํค๊ฐ ํญ์ ๋ค์ชฝ ํ ์ด๋ธ์ ์กด์ฌํ๊ธฐ ๋๋ฌธ์, ๋ฐ๋ํธ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๋ ํน์ดํ ๋ชจ์ต์ด ๋ํ๋ฉ๋๋ค.
์ด๋ป๊ฒ ๋งคํํ๋?
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String username;
...
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
...
}
@OneToMany
mappedBy ์์ฑ์ด ์์ด์ก๋ค๋ ๊ฒ์ ์ฃผ๋ชฉํด์ผ ํฉ๋๋ค.
Team์ด ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด๋ฏ๋ก mappedBy ์์ฑ์ ์ง์์ฃผ์์ต๋๋ค.
๋ํ @JoinColumn์ ๋ช ์ํด ์ฃผ์์ต๋๋ค.
@JoinColumn์ ์ฌ์ฉํ์ง ์์ผ๋ฉด JPA๋ ์ฐ๊ฒฐ ํ ์ด๋ธ์ ์ค๊ฐ์ ๋๊ณ ์ฐ๊ด๊ด๊ณ๋ฅผ ๊ด๋ฆฌํ๋ ์กฐ์ธ ํ ์ด๋ธ ์ ๋ต์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ์ฌ ๋งคํํ๊ธฐ ๋๋ฌธ์, ๊ผญ @JoinColumn์ ๋ถ์ฌ์ฃผ์ด์ผ ํฉ๋๋ค.
๐ณ ๋ฌธ์ ์
์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ์ ๋ฌธ์ ๋, ๋งคํํ ๊ฐ์ฒด๊ฐ ๊ด๋ฆฌํ๋ ์ธ๋ ํค๊ฐ ๋ค๋ฅธ ํ ์ด๋ธ์ ์๋ค๋ ์ ์ ๋๋ค.
์์ ์ ํ ์ด๋ธ์ ์ธ๋ ํค๊ฐ ์๋ค๋ฉด, ์ํฐํฐ์ ์ ์ฅ๊ณผ ์ฐ๊ด๊ด๊ณ ์ฒ๋ฆฌ๋ INSERT SQL ํ ๋ฒ์ผ๋ก ๋๋ผ ์ ์์ง๋ง, ๋ค๋ฅธ ํ ์ด๋ธ์ ์ธ๋ ํค๊ฐ ์์ผ๋ฉด, ์ฐ๊ด๊ด๊ณ ์ฒ๋ฆฌ๋ฅผ ์ํด UPDATE SQL์ ์ถ๊ฐ๋ก ๋ฐ์์ํต๋๋ค.
๋ํ ์์ ์ ํ ์ด๋ธ์ด ์๋ ๋ค๋ฅธ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋๊ธฐ ๋๋ฌธ์, ๊ด๋ฆฌ๊ฐ ๊ต์ฅํ ์ด๋ ค์์ง๋๋ค.
๐ณ update ์ฟผ๋ฆฌ ๋ฌธ์ ์ ํด๊ฒฐ - [updatable=false, nullable=false, cascade=PERSIST]
์์์ insert ์ฟผ๋ฆฌ ์ Update SQL์ด ์ถ๊ฐ๋ก ๋ฐ์ํ๋ ๋ฌธ์ ์ ์ updatable=false, nullable=false, cascade=PERSISTE๋ฅผ ํตํด ํด๊ฒฐํ ์ ์์ต๋๋ค.
@Entity
@Getter
@NoArgsConstructor
public class Board {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String boardTitle;
private String content;
@Enumerated
private BoardTags boardTags = new BoardTags();
public Board(String boardTitle, String content) {
this.boardTitle = boardTitle;
this.content = content;
}
public void addTag(BoardTag boardTag) {
boardTags.addTag(boardTag);
}
public void deleteAll() {
boardTags.boardTags().remove(0);
}
}
@Getter
@Entity
public class BoardTag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public BoardTag() {
}
public BoardTag(String name) {
this.name = name;
}
}
@Embeddable
public class BoardTags {
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "board_id", updatable = false, nullable = false)
private List<BoardTag> boardTags = new ArrayList<>();
public BoardTags() {
}
public List<BoardTag> boardTags() {
return boardTags;
}
public void addTag(BoardTag boardTag) {
this.boardTags.add(boardTag);
}
public void deleteAll() {
this.boardTags.clear();
}
}
@SpringBootTest
@Transactional
class BoardTagRepositoryTest {
@Autowired
private BoardTagRepository boardTagRepository;
@Autowired
private BoardRepository boardRepository;
@Autowired
private EntityManager em;
@Test
@Rollback(value = false)
void test() {
BoardTag ํ๊ทธ1 = new BoardTag("ํ๊ทธ1");
BoardTag ํ๊ทธ2 = new BoardTag("ํ๊ทธ2");
Board board = new Board("์ ๋ชฉ", "๋ด์ฉ");
board.addTag(ํ๊ทธ1);
board.addTag(ํ๊ทธ2);
boardRepository.save(board);
em.flush();
em.clear();
}
}
์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ๋ณด๋ค๋ ๋ค๋์ผ ์๋ฐฉํฅ ๋งคํ์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ์ ์ฌ์ฉํ๋ฉด, ์์ ์ด ๋งคํ๋ ํ ์ด๋ธ์ด ์๋ ๋ค๋ฅธ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๊ด๋ฆฌํ๊ฒ ๋๋ฏ๋ก ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ๋ฐ์์ํฌ ๋ฟ๋๋ฌ ๊ด๋ฆฌ๋ ์ด๋ ต๊ฒ ๋ง๋ญ๋๋ค.
๋ฐ๋ผ์ ์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ๋ณด๋ค๋, ๋ค๋์ผ ์๋ฐฉํฅ ๊ด๊ณ๋ฅผ ์ฌ์ฉํ๋๋ก ํ๋๊ฒ์ด ์ข์ต๋๋ค.
๐ณ ์ผ๋๋ค ์๋ฐฉํฅ
์ฌ์ค ์ผ๋๋ค ์๋ฐฉํฅ ๋งคํ์ ์กด์ฌํ์ง ์์ต๋๋ค. (์ผ๋๋ค ์๋ฐฉํฅ๊ณผ ๋ค๋์ผ ์๋ฐฉํฅ์ ์ฌ์ค ๋๊ฐ์ ๋ง์ ๋๋ค.)
์ ํํ๋ ์ผ๋๋ค ์๋ฐฉํฅ ๋งคํ์์ @OneToMany๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ํน์ฑ์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ด ๋ ์ ์์ต๋๋ค.
์๋ํ๋ฉด ์ผ๋๋ค ๊ด๊ณ์์๋ ํญ์ ๋ค ์ชฝ์ ์ธ๋ ํค๊ฐ ์กด์ฌํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
์ด๋ฐ ์ด์ ๋ก @ManyToOne์๋ mappedBy ์์ฑ์ด ์์ต๋๋ค.
๊ทธ๋ฌ๋ ์ ๋ง ์ผ๋๋ค ์๋ฐฉํฅ ๋งคํ์ ํ๊ณ ์ถ๋ค๋ฉด, ๋ฐฉ๋ฒ์ด ์์ ์๋๊ฒ์ ์๋๋๋ค.
์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ ๋ฐ๋ํธ์, ๊ฐ์ ์ธ๋ํค๋ฅผ ์ฌ์ฉํ๋ ๋ค๋์ผ ๋จ๋ฐฉํฅ ๋งคํ์ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ํ๋ ์ถ๊ฐํด์ฃผ๋ฉด ๋ฉ๋๋ค.
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) //์ฝ๊ธฐ ์ ์ฉ
private Team team;
...
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
@OneToMany
@JoinColumn(name = "TEAM_ID")
private List<Member> members = new ArrayList<>();
...
}
์์ฒ๋ผ ๊ฐ ์ํฐํฐ์ @JoinColumn์ ๋ช ์ํ์ฌ ๋ ์ํฐํฐ๊ฐ ๊ฐ์ ์ธ๋ํค๋ฅผ ๊ด๋ฆฌํ๋๋ก ๋ง๋ค์ด์ค ํ, ๋ค(N)์ชฝ ์ํฐํฐ๋ฅผ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ํ๋ ๋ง๋ค์ด์ฃผ์์ต๋๋ค.
๊ฐ์ ์ธ๋ํค๋ฅผ ์ฌ์ฉํด์ผ๋ง ๊ฐ๋ฅํ ๋ฐฉ๋ฒ์ ๋๋ค.
์ด ๋ฐฉ๋ฒ๋ ์ญ์ ์ผ๋๋ค ๋จ๋ฐฉํฅ ๋งคํ์ ๋จ์ ์ ๊ทธ๋๋ก ๊ฐ์ง๋ฏ๋ก, ๋ ์ ์์ผ๋ฉด ๋ค๋์ผ ์๋ฐฉํฅ ๋งคํ์ ์ฌ์ฉํ๋๋ก ํ๋๊ฒ์ด ์ข์ต๋๋ค.
๐ง ์ผ๋์ผ [1 : 1] : @OneToOne
์ผ๋์ผ ๊ด๊ณ๋ ์์ชฝ์ด ์๋ก ํ๋์ ๊ด๊ณ๋ง์ ๊ฐ์ง๋๋ค.
์ผ๋์ผ ๊ด๊ณ์ ๋ฐ๋๋ ์ผ๋์ผ ๊ด๊ณ์ด๋ฉฐ, ์ผ๋์ผ ๊ด๊ณ๋ ๋ ํ ์ด๋ธ ์ค ์ด๋๊ณณ์์๋ ์ธ๋ ํค๋ฅผ ๊ฐ์ง ์ ์์ต๋๋ค.
์ผ๋์ผ ๊ด๊ณ์ผ ๊ฒฝ์ฐ ๊ณ ๋ คํด์ผ ํ ์์๊ฐ ํ๋ ๋ ๋์ด๋ฉ๋๋ค.
๐ณ ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค
์ฃผ ๊ฐ์ฒด๊ฐ ๋์ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ๋ ๊ฒ์ฒ๋ผ, ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๋๊ณ ๋์ ํ ์ด๋ธ์ ์ฐธ์กฐํ๋ ๋ฐฉ๋ฒ์ ๋๋ค.
์ฅ์ ์ ์ฃผ ํ ์ด๋ธ๋ง ํ์ธํด๋ ๋์ ํ ์ด๋ธ๊ณผ ์ฐ๊ด๊ด๊ณ๊ฐ ์๋์ง ์ ์ ์๋ค๋ ์ ์ด๋ฉฐ, ๊ฐ์ฒด ์ฐธ์กฐ์ ์ธ๋ ํค๋ฅผ ๋น์ทํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํด๋น ๋ฐฉ๋ฒ์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ ์ธ๋ ํค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ ๋ํฌ ์ ์ฝ์กฐ๊ฑด์ ์ถ๊ฐํด์ฃผ์ด์ผ ํฉ๋๋ค.
(์ด์ธ์๋ ์ผ๋๋ค, ๋ค๋์ผ๊ณผ ๊ต์ฅํ ๋น์ทํฉ๋๋ค)
์๋ ์์์์ ์ฃผ ํ ์ด๋ธ์ MEMBER์ด๊ณ , ๋์ ํ ์ด๋ธ์ LOCKER ์ ๋๋ค.
๐ณ ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค - ๋จ๋ฐฉํฅ
์ฝ๋๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@OneToOne
@JoinColumn(name = "LOCKER_ID")
private Locker locker;
}
@Entity
public class Locker {
@Id @GeneratedValue
@Column(name = "LOCKER_ID")
private Long id;
private String name;
}
์ผ๋์ผ ๊ด๊ณ์ด๋ฏ๋ก @OneToOne์ ์ฌ์ฉํด ๋งคํํ์๊ณ , ๋ฐ์ดํฐ๋ฒ ์ด์ค์๋ LOCKER_ID(FK)์ ์ ๋ํฌ ์ ์ฝ์กฐ๊ฑด์ ์ถ๊ฐํ์์ต๋๋ค.
๋ค๋์ผ, ์ผ๋๋ค์ ๊ต์ฅํ ๋น์ทํ๊ณ , ์ด๋ ธํ ์ด์ ์๋ง ์ฐจ์ด๊ฐ ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๐ณ ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค - ์๋ฐฉํฅ
์ฝ๋๋ก ํํํ๋ฉด ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@OneToOne
@JoinColumn(name = "locker_ID")
private Locker locker;
}
@Entity
public class Locker {
@Id @GeneratedValue
@Column(name = "LOCKER_ID")
private Long id;
private String name;
@OneToOne(mappedBy = "locker")
private Member memeber;
}
์๋ฐฉํฅ ์ฐ๊ด๊ด๊ณ์ด๋ฏ๋ก, mappedBy๋ฅผ ์ฌ์ฉํ์ฌ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ์ ํด์ฃผ์์ต๋๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ๋ค๋์ผ, ์ผ๋๋ค์ ๊ต์ฅํ ๋น์ทํ๊ณ , ์ด๋ ธํ ์ด์ ์๋ง ์ฐจ์ด๊ฐ ์๋ค๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
๐ณ ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค
๋ฐ์ดํฐ๋ฒ ์ด์ค ๊ฐ๋ฐ์๋ค์ด ์ ํธํ๋ ๋ฐฉ์์ ๋๋ค.
ํ ์ด๋ธ ๊ด๊ณ๋ฅผ ์ผ๋์ผ์์ ์ผ๋๋ค ๊ด๊ณ๋ก ๋ณ๊ฒฝํ ๋, ํ ์ด๋ธ์ ๊ตฌ์กฐ๋ฅผ ๊ทธ๋๋ก ์ ์งํ ์ ์๋ค๋ ์ฅ์ ์ด ์์ต๋๋ค.
(์ผ๋๋ค์์๋ ๋ค ์ชฝ์ด ํญ์ ์ธ๋ํค๋ฅผ ๊ฐ์ง๋ฏ๋ก, ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค๊ฐ ์๋ค๋ฉด, ํ ์ด๋ธ์ ๊ตฌ์กฐ๊ฐ ์ ์ง๋ฉ๋๋ค.)
๐ณ ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค - ๋จ๋ฐฉํฅ
์ผ๋์ผ ๊ด๊ณ ์ค ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค๊ฐ ์๋ ๋จ๋ฐฉํฅ ๊ด๊ณ๋ ์ง์ํ์ง ์์ต๋๋ค.
๐ณ ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค - ์๋ฐฉํฅ
์ฌ์ค ์ด ๊ด๊ณ๋ ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค - ์๋ฐฉํฅ๊ณผ ๋์ผํฉ๋๋ค.
๋จ์ง ์ฃผ ํ ์ด๋ธ๋ง ๋ฐ๋์์ ๋ฟ์ ๋๋ค.
@Entity
public class Locker {
@Id @GeneratedValue
@Column(name = "LOCKER_ID")
private Long id;
private String name;
@OneToOne
@JoinColumn(name = "MEMBER_ID")
private Member memeber;
}
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String username;
@OneToOne(mappedBy = "member")
private Locker locker;
}
๐ง ์ฐธ๊ณ - ์ผ๋์ผ ๊ด๊ณ์ ์ฃผ์์ฌํญ
ํ๋ก์๋ฅผ ์ฌ์ฉํ ๋ ์ธ๋ ํค๋ฅผ ์ง์ ๊ด๋ฆฌํ์ง ์๋ ์ผ๋์ผ ๊ด๊ณ๋ ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ํ๋๋ผ๋ ์ฆ์ ๋ก๋ฉ๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด ์์ ์์ ์์ Locker.member๋ ์ง์ฐ ๋ก๋ฉํ ์ ์์ง๋ง, Member.locker๋ ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ํ๋๋ผ๋ ์ฆ์ ๋ก๋ฉ๋ฉ๋๋ค.
์ด๊ฒ์ ํ๋ก์์ ํ๊ณ ๋๋ฌธ์ ๋ฐ์ํ๋ ๋ฌธ์ ์ ๋๋ค.
์์ ์์๋ฅผ ๋ค์ด ๊ฐ๋จํ ์ค๋ช ํด๋ณด๊ฒ ์ต๋๋ค.
JPA์์๋ Member ๊ฐ์ฒด์ Locker ํ๋์ ํ๋ก์๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋, DB์ ๊ทธ ๊ฐ์ด ์๋์ง ์๋์ง ํ์ธํด์ผ ํฉ๋๋ค.
(์์ผ๋ฉด ํ๋ก์๋ฅผ ๋ฃ๊ณ , ์์ผ๋ฉด null์ ๋ฃ์ต๋๋ค)
๊ทธ๋ฌ๋ ์์ ๊ด๊ณ์์๋ LOCKER ํ ์ด๋ธ์ FK๊ฐ ์์ผ๋ฏ๋ก, ๊ฐ์ ํ์ธํ๊ธฐ ์ํด์๋ LOCKER ํ ์ด๋ธ์ ์กฐํํด์ผ ํ๋๊ฒ์ ๋๋ค.
์ฆ ์ด์ฐจํผ ์ฟผ๋ฆฌ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์, ๊ตณ์ด ํ๋ก์๋ก ๋ง๋ค ์ด์ ๊ฐ ์๋ ๊ฒ์ ๋๋ค.
๋ฐ๋ผ์ ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ํ๋๋ผ๋ ์ฆ์ ๋ก๋ฉ๋ฉ๋๋ค.
์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์๋ ํ๋ก์ ๋์ bytecode instrumentation์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ผ๋,, ๊ต์ฅํ ๋ณต์กํ๋ค๊ณ ํฉ๋๋ค.
๊ถ๊ธํ๋ค๋ฉด ๋ค์์ ์ฐธ๊ณ ํด์ฃผ์ธ์
๐ง ์ผ๋์ผ ์ฐ๊ด๊ด๊ณ ์ ๋ฆฌ
๐ณ ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค
- ์ฃผ ๊ฐ์ฒด๊ฐ ๋์ ๊ฐ์ฒด์ ์ฐธ์กฐ๋ฅผ ๊ฐ์ง๋ ๊ฒ ์ฒ๋ผ, ์ฃผ ํ ์ด๋ธ์ ์ธ๋ ํค๋ฅผ ๋๊ณ ๋์ ํ ์ด๋ธ์ ์ฐพ์
- ๊ฐ์ฒด์งํฅ ๊ฐ๋ฐ์ ์ ํธ
- JPA ๋งคํ์ด ํธ๋ฆฌํฉ๋๋ค.
- ์ฅ์ : ์ฃผ ํ ์ด๋ธ๋ง ์กฐํํด๋ ๋์ ํ ์ด๋ธ์ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธ ๊ฐ๋ฅ(์ฑ๋ฅ์์ผ๋ก ์ข๋ค)
- ๋จ์ : ๊ฐ์ด ์์ผ๋ฉด ์ธ๋ ํค์ null์ด ํ์ฉ๋ฉ๋๋ค.
๐ณ ๋์ ํ ์ด๋ธ์ ์ธ๋ ํค
- DB ๊ฐ๋ฐ์ ์ ํธ
- ์ฅ์ : ์ฃผ ํ ์ด๋ธ๊ณผ ๋์ ํ ์ด๋ธ์ ๊ด๊ณ๋ฅผ 1๋1์์ 1๋๋ค ๊ด๊ณ๋ก ๋ณ๊ฒฝํ ๋ ํ ์ด๋ธ ๊ตฌ์กฐ๊ฐ ์ ์ง
- ๋จ์ : ํ๋ก์ ๊ธฐ๋ฅ์ ํ๊ณ๋ก ์ง์ฐ ๋ก๋ฉ์ผ๋ก ์ค์ ํด๋ ํญ์ ์ฆ์ ๋ก๋ฉ๋ฉ๋๋ค
๐ณ mappedBy๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด...
mappedBy๋ฅผ ์ฌ์ฉํ์ง ์์ผ๋ฉด ์ผ๋์ผ ๊ด๊ณ์ ๊ฒฝ์ฐ ๊ฐ๊ฐ์ ํ ์ด๋ธ์ ์๋ก๋ฅผ ์ฐธ์กฐํ๋ FK๊ฐ ์ค์ ๋ฉ๋๋ค.
์ผ๋์ผ ๊ด๊ณ ์์
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@OneToOne
private Team team;
protected Member() {
}
public void setTeam(Team team) {
this.team = team;
}
}
@Entity
public class Team {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@OneToOne
private Member members;
}
๐ง ๋ค๋๋ค[N : N] : @ManyToMany
์ ์ฐ๋๊ฒ ์ข์ต๋๋ค.
์๋ ๊ทธ๋ฅ ์ฐ์ง๋ง์ธ์.
๐ณ ๋ค๋๋ค ๊ด๊ณ ๋์ ์ฐ๊ฒฐ ์ํฐํฐ๋ฅผ ์ฌ์ฉ
๋ ํ ์ด๋ธ์ ์ฐ๊ฒฐํ๋ ์ฐ๊ฒฐ ํ ์ด๋ธ์ ๋ง๋ค๊ณ , ๊ทธ๊ฒ์ ์ฐ๊ฒฐ ์ํฐํฐ๋ก ์น๊ฒฉ์์ผ์ค๋๋ค.
์ฆ @ManyToMany -> @OneToMany์ @ManyToOne์ผ๋ก ๋๋ ์ง๋ ๊ฒ์ ๋๋ค.
๐ง ์๋ฐฉํฅ ๋งคํ TIP
- ๋จ๋ฐฉํฅ ๋งคํ๋ง์ผ๋ก๋ ์ด๋ฏธ ์ฐ๊ด๊ด๊ณ ๋งคํ์ ์๋ฃ๋ ์ํ์ ๋๋ค.
- ๋จ๋ฐฉํฅ ๋งคํ์ผ๋ก ๋จผ์ ์ค๊ณ๋ฅผ ๋๋ธ ํ ํ์ํ ๊ฒฝ์ฐ์๋ง ์๋ฐฉํฅ์ ์ถ๊ฐํด๋ ๋ฉ๋๋ค. (์ด๋ DB ํ ์ด๋ธ์ ์ํฅ์ ์ฃผ์ง ์์ต๋๋ค.)
๐ Reference
[์๋ฐ ORM ํ์ค JPA ํ๋ก๊ทธ๋๋ฐ - ๊น์ํ]
'๐๏ธ Spring > JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[JPA] @MappedSuperclass (0) | 2021.12.15 |
---|---|
[JPA] ์์๊ด๊ณ ๋งคํํ๋ ๋ฐฉ๋ฒ (0) | 2021.12.15 |
[JPA] ์ฐ๊ด๊ด๊ณ ํธ์ ๋ฉ์๋ ์์ฑํ๊ธฐ (0) | 2021.12.14 |
[JPA] - @JoinColumn๊ณผ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ (mappedBy) (2) | 2021.12.14 |
[JPA] SEQUENCE (& TABLE) ์ ๋ต์์ ์ฒซ ํธ์ถ์ด 2๋ฒ ์ด๋ฃจ์ด์ง๋ ์ด์ (0) | 2021.12.14 |