Entity는 데이터베이스 테이블과 매핑되는 java class이다. 모델 또는 도메인 모델이라고 부르기도 한다.
[파일명: Question.java]
package com.mysite.sbb.sbbvscode.question;
import java.time.LocalDateTime;
import java.util.List;
import com.mysite.sbb.sbbvscode.answer.Answer;
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;
}
@Entity컨트롤러에 @Controller annotation을 적용하는 것처럼 엔티티에는 @Entity annotation을 적용해야 한다. 이 annotation을 적용해야 JPA가 entity로 인식한다.
@Id@Id annotation을 적용한 속성은 기본 키(primary key)로 지정된다. 기본 키로 지정하면 데이터베이스에 저장할 때 중복된 값을 가질 수 없다. 위에서 고유번호 id 속성은 각 데이터를 구분하는 유효한 값으로, 중복되면 안 되기 때문에 기본 키로 지정한 것이다.
@Generated Value@Generated Value annotation을 적용하면 데이터를 저장할 때, 해당 속성 값이 자동으로 1씩 증가하여 저장된다. stategy는 고유 번호를 생성하는 옵션이고 GenerationType.IDENTITY는 해당 column만의 독립적인 시퀀스를 생성하여 번호를 증가시킬 때 사용한다. strategy 옵션을 생략할 경우, @Generated Value annotation이 지정된 column들이 모두 동일한 시퀀스 번호를 생성하기 때문에 일정한 고유번호를 가질 수 없게 된다. 따라서 일반적으로 GenerationType.IDENTITY를 많이 사용한다.
@Columnentity의 속성은 데이터베이스 table의 column명과 일치하는데, column의 세부 설정을 위해 @Colomn annotation을 사용한다. length는 컬럼의 길이를 설정할 때 사용하고, columnDefinition은 column의 속성을 정의할 때 사용한다.columnDefinition="TEXT"는 "내용"처럼 글자 수를 제한할 수 없는 경우에 사용한다.
💡 엔티티의 속성은
@Column애너테이션을 사용하지 않더라도 테이블 컬럼으로 인식한다. 테이블 컬럼으로 인식하고 싶지 않은 경우에만@Transient애너테이션을 사용한다.
💡 엔티티와 Setter
일반적으로 entity에는 setter 메서드를 구현하지 않고 사용하기를 권한다. 왜냐하면 entity는 데이터베이스와 바로 연결되어 있으므로 데이터를 자유롭게 변경할 수 있는 setter 메서드를 허용하는 것이 안전하지 않다고 판단하기 때문이다.
그렇다면 setter 메서드 없이 어떻게 entity에 값을 저장할 수 있을까?
- entity를 생성 -> lombok의
@builderannotation을 통한 빌드 패턴을 사용- 데이터 변경 -> 그에 해당되는 메서드를 entity에 추가하여 데이터를 변경
@OneToManyquestion과 answer는 1:N 관계이므로 @OneToMany annotation을 사용한다. mappedBy는 참조 엔티티의 속성명을 의미한다. 여기서는 Answer entity에서 Question entity를 참조한 속성인 question을 전달해주면 된다.
이제 질문 객체에서 답변을 참조하려면 question.getAnswerList()를 호출하면 된다.
cascade = CascadeType.REMOVE @OneToMany의 속성으로, 질문을 삭제할 때 해당 질문에 달린 답변들도 모두 함께 삭제하기 위해 사용했다.
[파일명: Answer.java]
package com.mysite.sbb.sbbvscode.answer;
import java.time.LocalDateTime;
import org.springframework.data.annotation.CreatedDate;
import com.mysite.sbb.sbbvscode.question.Question;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
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;
@CreatedDate
private LocalDateTime createDate;
@ManyToOne
private Question question;
}
@ManyToOne@ManyToOne annotation은 N:1 관계 또는 부모 자식 관계를 갖는 구조에서 사용한다. 여기서는 질문 하나에 여러 개의 답변이 달릴 수 있는 구조이므로, Answer이 many(N 또는 자식), Question이 one(1 또는 부모)이 된다.answer.getQuestion().getSubject()처럼 접근할 수 있지만, question entity와 연결된 속성이라는 것을 명시적으로 표시하기 위해 question 속성에 @ManyToOne annotation을 추가해야 한다.entity 작성 후 H2 콘솔에 접속해보자.
