참고 링크 : https://wikidocs.net/book/7601
오라클DB와 intelliJ로 작업하였습니다
엔티티 Entity
엔티티는 모델 또는 도메인 모델이라고 부르기도 한다.
여기서는 이것들을 구분하지 않고 테이블과 매핑되는 클래스를 엔티티라 지칭하겠다.
질문 엔티티 작성하기
package com.gosari.repick_project.question;
import com.gosari.repick_project.answer.Answer;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Setter
@Getter
@Entity
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String subject;
private String content;
@Column(insertable = false, updatable = false, columnDefinition = "date default LOCALTIMESTAMP")
private Date createDate;
}
@Id
고유 번호 id 속성에 적용한 @Id 애너테이션은 id 속성을 기본 키로 지정한다.
기본 키로 지정하면 이제 id 속성의 값은 데이터베이스에 저장할 때 동일한 값으로 저장할 수 없다.
고유 번호를 기본 키로 한 이유는 고유 번호는 엔티티에서 각 데이터를 구분하는 유효한 값으로
중복되면 안 되기 때문이다.
@GeneratedValue
@GeneratedValue 애너테이션을 적용하면 데이터를 저장할 때 해당 속성에 값을 따로 세팅하지 않아도 1씩 자동으로 증가하여 저장된다. strategy는 고유번호를 생성하는 옵션
@GeneratedValue(starategy = GenerationType.SEQUENCE
SEQUENCE 전략의 특징
- 시퀀스오브젝트를 만들고 이걸 통해 id 값을 가져온다. (오라클만 가능!!)
- 데이터베이스 시퀀트 오브젝트를 사용하고, 시퀀스를 지원하는 oracle의 경우에 사용
- @SequenceGenerator 필요
@Column
엔티티의 속성은 테이블의 컬럼명과 일치하는데 컬럼의 세부 설정을 위해 @Column 애너테이션을 사용한다.
엔티티의 속성은 @Column 애너테이션을 사용하지 않더라도 테이블 컬럼으로 인식한다.
테이블 컬럼으로 인식하고 싶지 않은 경우에만 @Transient 애너테이션을 사용한다.
*컬럼: 각각의 데이터 값에 대한 열(↕)을 말함. 하나의 열에 데이터가 들어가는 속성을 의미하기도 함
- updatable 속성> 하이버네이트가 UPDATE SQL을 제너레이션 할때 writer, regDate, cnt칼럼은 제외하도록 추가
- insertable 속성> regDate, cnt 칼럼은 기본값을 설정했기 때문에 INSERT SQL에서 제외되도록 추가
- default 설정> createDate와 cnt 칼럼에는 default 설정을 추가하여 null대신 기본값이 설정되도록 함
테이블의 컬럼명
위의 Question 엔티티에서 작성일시에 해당하는 createDate 속성의 실제 테이블의 컬럼명은 create_date가 된다. 즉 createDate처럼 대소문자 형태의 카멜케이스(Camel Case) 이름은 create_date 처럼 모두 소문자로 변경되고 언더바(_)로 단어가 구분되어 실제 테이블 컬럼명이 된다.
*원본 글에서 length 와 TEXT 뺀 이유,
오라클에선 text 타입이 존재하지 않아 지원되지 않는다.
답변 엔티티 작성하기
package com.gosari.repick_project.answer;
import com.gosari.repick_project.question.Question;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
@Setter
@Getter
@Entity
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String content;
@Column(insertable = false, updatable = false, columnDefinition = "date default LOCALTIMESTAMP")
private Date createDate;
private Question question; //답변엔티티에 질문엔티티참조 (질문1:답변N)
}
question 속성은 답변 엔티티에서 질문 엔티티를 참조하기 위해 추가했다. 예를 들어 답변 객체(예:answer)를 통해 질문 객체의 제목을 알고 싶다면 answer.getQuestion().getSubject()처럼 접근할 수 있다. 하지만 이렇게 속성만 추가하면 안되고 질문 엔티티와 연결된 속성이라는 것을 명시적으로 표시해야 한다.
즉, 다음과 같이 question 속성에 @ManyToOne 애너테이션을 추가해야 한다.
답변 엔티티 수정하기
package com.gosari.repick_project.answer;
import com.gosari.repick_project.question.Question;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
@Setter
@Getter
@Entity
public class Answer {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String content;
@Column(insertable = false, updatable = false, columnDefinition = "date default LOCALTIMESTAMP")
private Date createDate;
@ManyToOne
private Question question; //답변엔티티에 질문엔티티참조 (질문1:답변N)
}
답변은 하나의 질문에 여러개가 달릴 수 있는 구조이다. 따라서 답변은 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 엔티티를 다음과 같이 수정하자.
질문 엔티티 수정하기
package com.gosari.repick_project.question;
import com.gosari.repick_project.answer.Answer;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
@Setter
@Getter
@Entity
public class Question {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Integer id;
private String subject;
private String content;
@Column(insertable = false, updatable = false, columnDefinition = "date default LOCALTIMESTAMP")
private Date createDate;
@OneToMany(mappedBy = "question", cascade = CascadeType.REMOVE)
private List<Answer>answerList; //질문엔티티에 답변엔티티참조 (질문1:답변N)
}
이제 질문 객체(예:question)에서 답변을 참조하려면 question.getAnswerList()를 호출하면 된다.
Answer 엔티티에서 Question 엔티티를 참조한 속성명 question을 mappedBy에 전달해야 한다.
CascadeType.REMOVE
질문 하나에는 여러개의 답변이 작성될 수 있다. 이때 질문을 삭제하면 그에 달린 답변들도 모두 함께 삭제하기 위해서 @OneToMany의 속성으로 cascade = CascadeType.REMOVE를 사용했다.
테이블 확인하기
Question과 Answer 엔티티를 작성한후 DB관리툴에 접속해 보자.
Question과 Answer 테이블이 자동으로 생성된 것을 확인할 수 있다.
만약 테이블이 생성되지 않았다면 로컬 서버를 재시작해 보자.
https://okky.kr/article/1020254 사이트에서 맨 밑 답변을 보면 오라클에서는 session 정보 시간대 변경이 불가하니 sysdate 말고 맞는 기준대의 컬럼 (current_date, current_timestamp, localtimestamp...)을 사용해야 함
SELECT SYSDATE, SESSIONTIMEZONE, CURRENT_DATE, CURRENT_TIMESTAMP, LOCALTIMESTAMP FROM DUAL;
'Follow Work > SpringbootBoard' 카테고리의 다른 글
[StringBoot] 서비스 (6) (0) | 2022.08.11 |
---|---|
[StringBoot] ROOT URL (5) (0) | 2022.08.11 |
[StringBoot] 질문 목록과 템플릿 (4) (0) | 2022.08.11 |
[StringBoot] 리포지터리 (3) (0) | 2022.08.11 |
[StringBoot] 프로젝트 설정 (1) (0) | 2022.08.11 |