스프링부트

2-4 엔티티(Entity)

늦게 시작한 개발자 2022. 12. 16. 10:25

엔티티는 처음 알게 된 것은 프로젝트를 진행했을때 알게 되었는데 현재 정리된 것은 개인 github에 정리를 해두었다 나중에 스프링부트 과정이 끝나게 된다면 그때 한번 포스팅에 정리해서 올려볼 예정이다.

엔티티는 데이터베이스 테이블과 매핑되는 자바 클래스를 지칭한다.

 

엔티티의 속성 구상하기
답변 엔티티 만들기

테이블 확인하기

엔티티의 속성 구상하기

질문(Question) 엔티티에는 최소한 아래와 같은 속성이 필요하다.

[표 1-1]

속성명 설명
id 질문의 고유 번호
subject 질문의 제목
content 질문의 내용
create_date 질문을 작성한 일시

 

마찬가지로 답변(Answer)엔티티에는 최소한 아래와 같은 속성이 필요하다.

[표 1-2]

속성명 설명
id 답변의 고유 번호
question 질문(어떤 질문의 답변인지 알아야하므로 질문 속성이 필요)
content 답변의 내용
create_date 답변을 작성한 일시

속성을 바탕으로 질문(Question)과 답변(Answer)에 해당되는 엔티티를 작성한다.

1-1 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버전을 사용하는 경우

import javax.* 처럼 jakarta 패키지로 시작하는 부분을 import javax.* 처럼 jakrata를 javax로 바꿔야 한다.

ex) import jakarata.*  - > import javax.*

 

엔티티로 만들고자 하여 Question 클래스에 @Entity 애너테이션을 적용. 그래야만 JPA가 엔티티로 인식한다. 그리고 Getter, Setter 메서드를 자동으로 생성하기 위해 롬복의 @Getter, @Setter 애너테이션을 적용했다.

참고로 컨트롤러에 @Controller 애너테이션 적용과 같은 것이라 보면 된다.

 

그리고 [표 1-1] 처럼 id(고유번호), subject(제목), createDate(입력일시)를 추가하였고 추가된 함수값에 대한 애너테이션에 대해 살펴보자.

@Id

애너테이션은 id 속성을 기본키로 지정하면 .id속성의 값은 데이터베이스에 저장할 때 동일한 값으로 저장할 수 없다. id를 기본 키로 한 이유는 id는 엔티티에서 각 데이터를 구분하는 유효한 값으로 중복되면 안되기 때문이다.

데이터베이스에서는 id와 같은 특징을 가진 속성을 기본 키(primary key)라고 부른다.
@GeneratedValue


애너테이션을 적용하면 데이터를 저장할 때 해당 속성에 값을 따로 세팅하지 않아도 1씩 자동으로 증가하여 저장된다.
strategy는 고유번호를 생성하는 옵션으로 GenerationType.IDENTITY는 해당 컬럼만의 독립적인 시퀀스를 생성하여 번호를 증가시킬때 사용된다.

만약 strategy 옵션을 생략할 경우에 @GeneratedValue 지정된 컬럼들이 모두 동일한 시퀀스 번호로 생성되기 때문에 각자만의 고유한 번호를 가질 수 없다. 그렇기 때문에 GenerationType.IDENTITY를 많이 사용하고 있다.
@Column


엔티티의 속성은 테이블의 컬럼명과 일치하는데 컬럼의 세부 설정을 위해 @Column 애너테이션을 사용한다.
length는 컬럼의 길이를 설정할때 사용. columnDefinition은 컬럼의 속성을 정의할 때 사용한다.
columnDefinition = "TEXT"은 "내용"처럼 글자수를 제한할 수없는 경우에 사용한다.

엔티티의 속성은 @Column 애너테이션을 사용하지 않더라도 테이블 컬럼으로 인식.
만약 테이블 컬럼으로 인식하기 싫을 경우에는 @Transient 애너테이션을 사용.

그렇다면 createDate 속성의 실제 테이블 컬럼명은? create_date가 된다. 대소문자 형태의 카멜케이스(Camel Case) 이름은 모두 소문자로 변경되고 언더바( _ )로 단어가 구분되어 실제 테이블 컬럼명이 된다.

 

엔티티에는 Setter 메서드를 구현하지 않고 사용하기를 권한다고 한다. 엔티티는 데이터베이스와 바로 연결이 되어 있으므로 데이터를 자유롭게 변경할 수 있는 Setter메서드를 허용하는 것이 안전하지 않다고 판단하기 때문!

 

Setter 메서드 없이 어떻게 엔티티에 값을 저장할까? 

롬복의 @Builder 애너테이션을 통한 빌드패턴을 사용하고, 데이터를 변경해야 할 경우에는 그에 해당되는 메서드를 엔티티에 추가하여 데이터를 변경하면 된다.

하지만 나는 초보자이기 때문에 혼동되는 것을 없애기 위해서 Setter 메서드를 추가하여 진행한다.

답변 엔티티 만들기

새로운 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;
}

private Question question; 에서 question 속성은 답변 엔티티에서 질문 엔티티를 참조하기 위해 추가된 것이다. 답변 객체를 통해 질문 객체의 제목을 알고 싶다면 answer.getQuestion().getSubject() 접근으로 확인할 수 있다. 하지만 반드시 저것만 작성하는게 아닌 질문 엔티티와 매핑되어 있다는 것을 명시해야 한다.

내가 작성한 것에 대해서 당연히 어떤것이 질문 엔티티고 답변 엔티티인지 구분이 가능하지만, 프로젝트를 하는 상황에서는 질문/답변이 아니게 되기도 하고 구분을 해야하기 때문에 매핑되었다는 것을 명시하는 것으로 보인다.

그렇기 때문에 Answer 클래스 question 속성에 @ManyToOne 애너테이션을 추가해 줘야 한다..

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 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;
	
	private LocalDateTime createDate;
	
	@ManyToOne
	private Question question;
}

2-1 ManyToOne 애너테이션 추가

질문 하나에 여러개의 답변이 달릴 수 있다. 따라서 답변은 Many(많은 것)가 되는 것이고 질문은 One(하나)가 되는 것이다. 따라서 @ManyToOne은 N:1의 관계가 되는 것이다. 이렇게 애너테이션을 설정하게 된다면 Answer 엔티티question 속성Question 엔티티가 서로 연결이 되는 것이다. (Question엔티티는 부모 / Answer엔티티는 자식)

 

반대로 Question 엔티티에서 Answer 엔티티를 참조할 수는 없는 것일까?

가능. 답변과 질문이 N:1이라고 한다면 질문과 답변은 1:N의 관계가 된다. 이럴 경우에는 @ManyToOne이 아닌 @OneToMany 애너테이션을 사용한다. Question 하나에 Answer는 여러개이므로 Question 엔티티에 추가할 답변의 속성은 List 형태로 구성해야 한다.

Question엔티티에서 수정을 해보자.

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;
}

2-2 Question 엔티티 수정 전
2-3 Question 엔티티 수정 후

Answer 엔티티 객체로 구성된 answerList를 속성으로 추가, @OneToMany 애너테이션 설정을 완료했다. 질문 객체에서 답변을 참조하려면, question.getAnswerList()를 호출하면 된다. mappedBy는 참조 엔티티의 속성명을 의미하며, Answer엔티티에서 Question엔티티를 참조한 속성명 questionmappedBy에 전달해야 한다.

CascadeType.REMOVE

질문 하나에는 여러개의 답변이 작성될 수 있는데, 질문을 삭제하면 그에 달린 답변들도 모두 함께 삭제 하기 위해서 @OneToMany의 속성으로 cascade = CascadeType.REMOVE를 사용하였다.

테이블 확인하기

Question, Answer 엔티티 작성이 완료 되었다면, H2 콘솔에 접속하자.

3-1 Answer/Question 엔티티 생성 및 속성까지 자동 생성 확인 완료