해당 게시글은 점프 투 스프링부트 교재를 통한 개인 학습 용도이며 기초 세팅은 생략하였습니다.
자바 8, 스프링부트 2.7.7버전입니다.
[ 엔티티 ]
엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스를 말한다.
엔티티는 모델 또는 도메인 모델 이라고 부르기도 한다.
질문 엔티티 작성하기
package com.example.board.Question;
import java.time.LocalDateTime;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import com.example.board.Answer.Answer;
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;
}
엔티티로 만들기 위해 Question 클래스에 @Entity 애너테이션을 적용했다.
@Entity 애너테이션을 적용해야 JPA가 엔티티로 인식한다.
Getter, Setter 메서드를 자동으로 생성하기 위해 롬복의 @Getter, @Setter 애너테이션을 적용했다.
@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 애너테이션을 사용한다.
테이블의 컬럼명
작성일시에 해당하는 createDate 속성의 실제 테이블의 컬럼명은 create_date가 된다.
createDate처럼 대소문자 형태의 카멜케이스(Camel Case) 이름은 create_date 처럼 모두 소문자로 변경되고 언더바(_)로 단어가 구분되어 실제 테이블 컬럼명이 된다.
엔티티와 Setter
일반적으로 엔티티에는 Setter 메서드를 구현하지 않고 사용하기를 권한다. 엔티티는 데이터베이스와 바로 연결되어 있으므로 데이터를 자유롭게 변경할 수 있는 Setter 메서드를 허용하는 것이 안전하지 않다고 판단하기 때문이다.
그렇다면 Setter 메서드 없이 어떻게 엔티티에 값을 저장할 수 있을까?
엔티티를 생성할 경우에는 롬복의 @Builder 어노테이션을 통한 빌드패턴을 사용하고, 데이터를 변경해야 할 경우에는 그에 해당되는 메서드를 엔티티에 추가하여 데이터를 변경하면 된다.
다만, 이 책은 복잡도를 낮추고 원활한 설명을 위해 엔티티에 Setter 메서드를 추가하여 진행하려 한다.
CascadeType.REMOVE
질문 하나에는 여러개의 답변이 작성될 수 있다. 이때 질문을 삭제하면 그에 달린 답변들도 모두 함께 삭제하기 위해서 @OneToMany의 속성으로 cascade = CascadeType.REMOVE를 사용했다.
Question 하나에 Answer는 여러개이므로 Question 엔티티에 추가할 답변의 속성은 List 형태로 구성해야 한다.
답변 엔티티 생성하기
package com.example.board.Answer;
import java.time.LocalDateTime;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import com.example.board.Question.Question;
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;
@ManyToOne
private Question question;
}
question 속성은 답변 엔티티에서 질문 엔티티를 참조하기 위해 추가했다.
예를 들어 답변 객체를 통해 질문 객체의 제목을 알고 싶다면 answer.getQuestion().getSubject()처럼 접근할 수 있다.
하지만 이렇게 속성만 추가하면 안되고 질문 엔티티와 연결된 속성이라는 것을 명시적으로 표시해야 한다.
즉, 다음과 같이 question 속성에 @ManyToOne 애너테이션을 추가해야 한다.
답변은 하나의 질문에 여러개가 달릴 수 있는 구조이다. 따라서 답변은 Many가 되고 질문은 One이 된다.
@ManyToOne 애너테이션을 설정하면 Answer 엔티티의 question 속성과 Question 엔티티가 서로 연결된다. (실제 데이터베이스에서는 ForeignKey 관계가 생성된다.)
@ManyToOne은 부모 자식 관계를 갖는 구조에서 사용한다.
여기서 부모는 Question, 자식은 Answer라고 할 수 있다.
테이블이 잘 생성된 것을 확인할 수 있다
2023.04 ~ 백엔드 개발자의 기록
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!