02D. ์ํฐํฐ
02D. ์ํฐํฐ ๊ด๋ จ
์ด์ SBB๊ฐ ์ฌ์ฉํ ์ํฐํฐ(Entity)์ ๋ง๋ค์ด ๋ณด์. ์ํฐํฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ๊ณผ ๋งคํ๋๋ ์๋ฐ ํด๋์ค๋ฅผ ๋งํ๋ค. SBB๋ ์ง๋ฌธ๊ณผ ๋ต๋ณ์ ํ ์ ์๋ ๊ฒ์ํ ์๋น์ค์ด๋ค. ๋ฐ๋ผ์ SBB์๋ ์ง๋ฌธ๊ณผ ๋ต๋ณ์ ํด๋นํ๋ ์ํฐํฐ๊ฐ ์์ด์ผ ํ๋ค.
์ํฐํฐ๋ ๋ชจ๋ธ ๋๋ ๋๋ฉ์ธ ๋ชจ๋ธ์ด๋ผ๊ณ ๋ถ๋ฅด๊ธฐ๋ ํ๋ค. ์ด ์ฑ ์์๋ ์ด๊ฒ๋ค์ ๊ตฌ๋ถํ์ง ์๊ณ ํ ์ด๋ธ๊ณผ ๋งคํ๋๋ ํด๋์ค๋ฅผ ์ํฐํฐ๋ผ ์ง์นญํ๊ฒ ๋ค.
์ํฐํฐ์ ์์ฑ ๊ตฌ์ํ๊ธฐ
๊ทธ๋ ๋ค๋ฉด ์ง๋ฌธ๊ณผ ๋ต๋ณ ์ํฐํฐ์๋ ์ด๋ค ์์ฑ๋ค์ด ํ์ํ์ง ๋จผ์ ์๊ฐํด ๋ณด์.
์ง๋ฌธ(Question
) ์ํฐํฐ์๋ ์ต์ํ ๋ค์๊ณผ ๊ฐ์ ์์ฑ์ด ํ์ํ๋ค.
์์ฑ๋ช | ์ค๋ช |
---|---|
id | ์ง๋ฌธ์ ๊ณ ์ ๋ฒํธ |
subject | ์ง๋ฌธ์ ์ ๋ชฉ |
content | ์ง๋ฌธ์ ๋ด์ฉ |
create_date | ์ง๋ฌธ์ ์์ฑํ ์ผ์ |
๋ง์ฐฌ๊ฐ์ง๋ก ๋ต๋ณ(Answer) ์ํฐํฐ์๋ ์ต์ํ ๋ค์๊ณผ ๊ฐ์ ์์ฑ์ด ํ์ํ๋ค.
์์ฑ๋ช | ์ค๋ช |
---|---|
id | ๋ต๋ณ์ ๊ณ ์ ๋ฒํธ |
question | ์ง๋ฌธ (์ด๋ค ์ง๋ฌธ์ ๋ต๋ณ์ธ์ง ์์์ผํ๋ฏ๋ก ์ง๋ฌธ ์์ฑ์ด ํ์ํ๋ค) |
content | ๋ต๋ณ์ ๋ด์ฉ |
create_date | ๋ต๋ณ์ ์์ฑํ ์ผ์ |
์ด๋ ๊ฒ ์๊ฐํ ์์ฑ์ ๋ฐํ์ผ๋ก ์ง๋ฌธ(Question
)๊ณผ ๋ต๋ณ(Answer
)์ ํด๋น๋๋ ์ํฐํฐ๋ฅผ ์์ฑํด ๋ณด์.
์ง๋ฌธ ์ํฐํฐ ์์ฑํ๊ธฐ
๋ค์๊ณผ ๊ฐ์ด Question ํด๋์ค๋ฅผ ์์ฑํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/
Question.java
package com.mysite.sbb;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 200)
private String subject;
@Column(columnDefinition = "TEXT")
private String content;
private LocalDateTime createDate;
}
์คํ๋ง๋ถํธ 2.x ๋ฒ์ ์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
์ด ์ฑ
์ ์คํ๋ง๋ถํธ 3.x ๋ฒ์ ์ ๊ธฐ์ค์ผ๋ก ์ค๋ช
ํ๋ค. ๋ง์ฝ ์์ ๋ฒ์ ์ธ ์คํ๋ง๋ถํธ 2.x ๋ฒ์ ์ ์ฌ์ฉํ๋ค๋ฉด import javax.*
์ฒ๋ผ jakarta ํจํค์ง๋ก ์์ํ๋ ๋ถ๋ถ์ import javax.*
์ฒ๋ผ jakarta๋ฅผ javax๋ก ๋ฐ๊พธ์ด์ผ ํ๋ค.
์ํฐํฐ๋ก ๋ง๋ค๊ธฐ ์ํด Question ํด๋์ค์ @Entity
์ ๋ํ
์ด์
์ ์ ์ฉํ๋ค. @Entity
์ ๋ํ
์ด์
์ ์ ์ฉํด์ผ JPA๊ฐ ์ํฐํฐ๋ก ์ธ์ํ๋ค. ๊ทธ๋ฆฌ๊ณ Getter, Setter ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ์์ฑํ๊ธฐ ์ํด ๋กฌ๋ณต์ @Getter
, @Setter
์ ๋ํ
์ด์
์ ์ ์ฉํ๋ค.
์ปจํธ๋กค๋ฌ์ @Controller
์ ๋ํ
์ด์
์ ์ ์ฉํ๋ ๊ฒ๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก ์ํฐํฐ๋ @Entity
์ ๋ํ
์ด์
์ ์ ์ฉํด์ผ ํ๋ค.
๊ทธ๋ฆฌ๊ณ ์ํฐํฐ์ ์์ฑ์ผ๋ก ๊ณ ์ ๋ฒํธ(id
), ์ ๋ชฉ(subject
), ๋ด์ฉ(content
), ์์ฑ์ผ์(createDate
)๋ฅผ ์ถ๊ฐํ๋ค. ๊ฐ ์์ฑ์๋ Id, GeneratedValue, Column๊ณผ ๊ฐ์ ์ ๋ํ
์ด์
์ด ์ ์ฉ๋์ด ์๋๋ฐ ๊ทธ๊ฒ๋ค์ ๋ํด์ ํ๋์ฉ ์์๋ณด์.
@Id
๊ณ ์ ๋ฒํธ id ์์ฑ์ ์ ์ฉํ @Id ์ ๋ํ ์ด์ ์ id ์์ฑ์ ๊ธฐ๋ณธ ํค๋ก ์ง์ ํ๋ค. ๊ธฐ๋ณธ ํค๋ก ์ง์ ํ๋ฉด ์ด์ id ์์ฑ์ ๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ ๋ ๋์ผํ ๊ฐ์ผ๋ก ์ ์ฅํ ์ ์๋ค. ๊ณ ์ ๋ฒํธ๋ฅผ ๊ธฐ๋ณธ ํค๋ก ํ ์ด์ ๋ ๊ณ ์ ๋ฒํธ๋ ์ํฐํฐ์์ ๊ฐ ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ถํ๋ ์ ํจํ ๊ฐ์ผ๋ก ์ค๋ณต๋๋ฉด ์ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋ id์ ๊ฐ์ ํน์ง์ ๊ฐ์ง ์์ฑ์ ๊ธฐ๋ณธ ํค(primary key)๋ผ๊ณ ํ๋ค.
@GeneratedValue
@GeneratedValue
์ ๋ํ
์ด์
์ ์ ์ฉํ๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ๋ ํด๋น ์์ฑ์ ๊ฐ์ ๋ฐ๋ก ์ธํ
ํ์ง ์์๋ 1์ฉ ์๋์ผ๋ก ์ฆ๊ฐํ์ฌ ์ ์ฅ๋๋ค. strategy
๋ ๊ณ ์ ๋ฒํธ๋ฅผ ์์ฑํ๋ ์ต์
์ผ๋ก GenerationType.IDENTITY
๋ ํด๋น ์ปฌ๋ผ๋ง์ ๋
๋ฆฝ์ ์ธ ์ํ์ค๋ฅผ ์์ฑํ์ฌ ๋ฒํธ๋ฅผ ์ฆ๊ฐ์ํฌ ๋ ์ฌ์ฉํ๋ค.
strategy
์ต์ ์ ์๋ตํ ๊ฒฝ์ฐ์@GeneratedValue
์ ๋ํ ์ด์ ์ด ์ง์ ๋ ์ปฌ๋ผ๋ค์ด ๋ชจ๋ ๋์ผํ ์ํ์ค๋ก ๋ฒํธ๋ฅผ ์์ฑํ๊ธฐ ๋๋ฌธ์ ์ผ์ ํ ์์์ ๊ณ ์ ๋ฒํธ๋ฅผ ๊ฐ์ง์ ์๊ฒ ๋๋ค. ์ด๋ฌํ ์ด์ ๋ก ๋ณดํตGenerationType.IDENTITY
๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค.
@Column
์ํฐํฐ์ ์์ฑ์ ํ
์ด๋ธ์ ์ปฌ๋ผ๋ช
๊ณผ ์ผ์นํ๋๋ฐ ์ปฌ๋ผ์ ์ธ๋ถ ์ค์ ์ ์ํด @Column
์ ๋ํ
์ด์
์ ์ฌ์ฉํ๋ค. length๋ ์ปฌ๋ผ์ ๊ธธ์ด๋ฅผ ์ค์ ํ ๋ ์ฌ์ฉํ๊ณ columnDefinition
์ ์ปฌ๋ผ์ ์์ฑ์ ์ ์ํ ๋ ์ฌ์ฉํ๋ค. columnDefinition = "TEXT"
์ "๋ด์ฉ"์ฒ๋ผ ๊ธ์ ์๋ฅผ ์ ํํ ์ ์๋ ๊ฒฝ์ฐ์ ์ฌ์ฉํ๋ค.
์ํฐํฐ์ ์์ฑ์
@Column
์ ๋ํ ์ด์ ์ ์ฌ์ฉํ์ง ์๋๋ผ๋ ํ ์ด๋ธ ์ปฌ๋ผ์ผ๋ก ์ธ์ํ๋ค. ํ ์ด๋ธ ์ปฌ๋ผ์ผ๋ก ์ธ์ํ๊ณ ์ถ์ง ์์ ๊ฒฝ์ฐ์๋ง@Transient
์ ๋ํ ์ด์ ์ ์ฌ์ฉํ๋ค.
ํ ์ด๋ธ์ ์ปฌ๋ผ๋ช
์์ Question ์ํฐํฐ์์ ์์ฑ์ผ์์ ํด๋นํ๋ createDate
์์ฑ์ ์ค์ ํ
์ด๋ธ์ ์ปฌ๋ผ๋ช
์ create_date
๊ฐ ๋๋ค. ์ฆ createDate
์ฒ๋ผ ๋์๋ฌธ์ ํํ์ ์นด๋ฉ์ผ์ด์ค(Camel Case) ์ด๋ฆ์ create_date
์ฒ๋ผ ๋ชจ๋ ์๋ฌธ์๋ก ๋ณ๊ฒฝ๋๊ณ ์ธ๋๋ฐ(_
)๋ก ๋จ์ด๊ฐ ๊ตฌ๋ถ๋์ด ์ค์ ํ
์ด๋ธ ์ปฌ๋ผ๋ช
์ด ๋๋ค.
์ํฐํฐ์ Setter
์ผ๋ฐ์ ์ผ๋ก ์ํฐํฐ์๋ Setter ๋ฉ์๋๋ฅผ ๊ตฌํํ์ง ์๊ณ ์ฌ์ฉํ๊ธฐ๋ฅผ ๊ถํ๋ค. ์๋ํ๋ฉด ์ํฐํฐ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ฐ๋ก ์ฐ๊ฒฐ๋์ด ์์ผ๋ฏ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ ๋กญ๊ฒ ๋ณ๊ฒฝํ ์ ์๋ Setter ๋ฉ์๋๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์์ ํ์ง ์๋ค๊ณ ํ๋จํ๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ ๋ค๋ฉด Setter ๋ฉ์๋ ์์ด ์ด๋ป๊ฒ ์ํฐํฐ์ ๊ฐ์ ์ ์ฅํ ์ ์์๊น?
์ํฐํฐ๋ฅผ ์์ฑํ ๊ฒฝ์ฐ์๋ ๋กฌ๋ณต์ @Builder
์ด๋
ธํ
์ด์
์ ํตํ ๋น๋ํจํด์ ์ฌ์ฉํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํด์ผ ํ ๊ฒฝ์ฐ์๋ ๊ทธ์ ํด๋น๋๋ ๋ฉ์๋๋ฅผ ์ํฐํฐ์ ์ถ๊ฐํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ฉด ๋๋ค.
๋ค๋ง, ์ด ์ฑ ์ ๋ณต์ก๋๋ฅผ ๋ฎ์ถ๊ณ ์ํํ ์ค๋ช ์ ์ํด ์ํฐํฐ์ Setter ๋ฉ์๋๋ฅผ ์ถ๊ฐํ์ฌ ์งํํ๋ ค ํ๋ค.
๋ต๋ณ ์ํฐํฐ ์์ฑํ๊ธฐ
์ด์ด์ ๋ค์๊ณผ ๊ฐ์ด ๋ต๋ณ ์ํฐํฐ๋ ์์ฑํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/
Answer.java
package com.mysite.sbb;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(columnDefinition = "TEXT")
private String content;
private LocalDateTime createDate;
private Question question;
}
Id
, content
, createDate
์์ฑ์ ์ง๋ฌธ ์ํฐํฐ์ ๋์ผํ๋ฏ๋ก ์ค๋ช
์ ์๋ตํ๋ค.
question
์์ฑ์ ๋ต๋ณ ์ํฐํฐ์์ ์ง๋ฌธ ์ํฐํฐ๋ฅผ ์ฐธ์กฐํ๊ธฐ ์ํด ์ถ๊ฐํ๋ค. ์๋ฅผ ๋ค์ด ๋ต๋ณ ๊ฐ์ฒด(์:answer)๋ฅผ ํตํด ์ง๋ฌธ ๊ฐ์ฒด์ ์ ๋ชฉ์ ์๊ณ ์ถ๋ค๋ฉด answer.getQuestion().getSubject()
์ฒ๋ผ ์ ๊ทผํ ์ ์๋ค. ํ์ง๋ง ์ด๋ ๊ฒ ์์ฑ๋ง ์ถ๊ฐํ๋ฉด ์๋๊ณ ์ง๋ฌธ ์ํฐํฐ์ ์ฐ๊ฒฐ๋ ์์ฑ์ด๋ผ๋ ๊ฒ์ ๋ช
์์ ์ผ๋ก ํ์ํด์ผ ํ๋ค.
์ฆ, ๋ค์๊ณผ ๊ฐ์ด question
์์ฑ์ @ManyToOne
์ ๋ํ
์ด์
์ ์ถ๊ฐํด์ผ ํ๋ค.
// (... ์๋ต ...)
import jakarta.persistence.ManyToOne;
// (... ์๋ต ...)
@Getter
@Setter
@Entity
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(columnDefinition = "TEXT")
private String content;
@CreatedDate
private LocalDateTime createDate;
@ManyToOne
private Question question;
}
๋ต๋ณ์ ํ๋์ ์ง๋ฌธ์ ์ฌ๋ฌ๊ฐ๊ฐ ๋ฌ๋ฆด ์ ์๋ ๊ตฌ์กฐ์ด๋ค. ๋ฐ๋ผ์ ๋ต๋ณ์ Many(๋ง์ ๊ฒ)๊ฐ ๋๊ณ ์ง๋ฌธ์ One(ํ๋)์ด ๋๋ค. ๋ฐ๋ผ์ @ManyToOne
์ N:1 ๊ด๊ณ๋ผ๊ณ ํ ์ ์๋ค. ์ด๋ ๊ฒ @ManyToOne
์ ๋ํ
์ด์
์ ์ค์ ํ๋ฉด Answer
์ํฐํฐ์ question ์์ฑ๊ณผ Question
์ํฐํฐ๊ฐ ์๋ก ์ฐ๊ฒฐ๋๋ค. (์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋ ForeignKey ๊ด๊ณ๊ฐ ์์ฑ๋๋ค.)
@ManyToOne
์ ๋ถ๋ชจ ์์ ๊ด๊ณ๋ฅผ ๊ฐ๋ ๊ตฌ์กฐ์์ ์ฌ์ฉํ๋ค. ์ฌ๊ธฐ์ ๋ถ๋ชจ๋Question
, ์์์Answer
๋ผ๊ณ ํ ์ ์๋ค.
๊ทธ๋ ๋ค๋ฉด ๋ฐ๋๋ฐฉํฅ, ์ฆ Question
์ํฐํฐ์์ Answer
์ํฐํฐ๋ฅผ ์ฐธ์กฐํ ์๋ ์์๊น?
๊ฐ๋ฅํ๋ค. ๋ต๋ณ๊ณผ ์ง๋ฌธ์ด N:1์ ๊ด๊ณ๋ผ๋ฉด ์ง๋ฌธ๊ณผ ๋ต๋ณ์ 1:N์ ๊ด๊ณ๋ผ๊ณ ํ ์ ์๋ค. ์ด๋ฐ๊ฒฝ์ฐ์๋ @ManyToOne
์ด ์๋ @OneToMany
์ ๋ํ
์ด์
์ ์ฌ์ฉํ๋ค. Question
ํ๋์ Answer
๋ ์ฌ๋ฌ๊ฐ์ด๋ฏ๋ก Question
์ํฐํฐ์ ์ถ๊ฐํ ๋ต๋ณ์ ์์ฑ์ List ํํ๋ก ๊ตฌ์ฑํด์ผ ํ๋ค.
์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด Question
์ํฐํฐ๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํ์.
ํ์ผ๋ช :
/sbb/src/main/java/com/mysite/sbb/
Question.java
package com.mysite.sbb;
import java.time.LocalDateTime;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(length = 200)
private String subject;
@Column(columnDefinition = "TEXT")
private String content;
private LocalDateTime createDate;
@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
private List<Answer> answerList;
}
Answer
์ํฐํฐ ๊ฐ์ฒด๋ก ๊ตฌ์ฑ๋ answerList๋ฅผ ์์ฑ์ผ๋ก ์ถ๊ฐํ๊ณ @OneToMany
์ ๋ํ
์ด์
์ ์ค์ ํ๋ค. ์ด์ ์ง๋ฌธ ๊ฐ์ฒด(์:question)์์ ๋ต๋ณ์ ์ฐธ์กฐํ๋ ค๋ฉด question.getAnswerList()
๋ฅผ ํธ์ถํ๋ฉด ๋๋ค. @OneToMany
์ ๋ํ
์ด์
์ ์ฌ์ฉ๋ mappedBy๋ ์ฐธ์กฐ ์ํฐํฐ์ ์์ฑ๋ช
์ ์๋ฏธํ๋ค. ์ฆ, Answer
์ํฐํฐ์์ Question
์ํฐํฐ๋ฅผ ์ฐธ์กฐํ ์์ฑ๋ช
question์ mappedBy์ ์ ๋ฌํด์ผ ํ๋ค.
CascadeType.REMOVE
์ง๋ฌธ ํ๋์๋ ์ฌ๋ฌ๊ฐ์ ๋ต๋ณ์ด ์์ฑ๋ ์ ์๋ค. ์ด๋ ์ง๋ฌธ์ ์ญ์ ํ๋ฉด ๊ทธ์ ๋ฌ๋ฆฐ ๋ต๋ณ๋ค๋ ๋ชจ๋ ํจ๊ป ์ญ์ ํ๊ธฐ ์ํด์ @OneToMany
์ ์์ฑ์ผ๋ก cascade = CascadeType.REMOVE
๋ฅผ ์ฌ์ฉํ๋ค.
ํ ์ด๋ธ ํ์ธํ๊ธฐ
Question
๊ณผ Answer
์ํฐํฐ๋ฅผ ์์ฑํํ H2 ์ฝ์์ ์ ์ํด ๋ณด์.
๋ง์ฝ ํ ์ด๋ธ์ด ์์ฑ๋์ง ์์๋ค๋ฉด ๋ก์ปฌ ์๋ฒ๋ฅผ ์ฌ์์ํด ๋ณด์.