학습 목표
1. JPA를 사용하여 데이터베이스 테이블과 자바 객체 간의 매핑을 정확하게 구현할 수 있다
2. 다대일(Many-to-One) 관계를 설정하여 엔티티 간의 연관성을 표현할 수 있다.
3. 데이터베이스에 저장할 필요가 없는 필드를 효과적으로 관리할 수 있다.

Reply.java
더보기
package com.tenco.blog_v1.reply;
import com.tenco.blog_v1.board.Board;
import com.tenco.blog_v1.user.User;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "reply_tb")
@ToString(exclude = {"user", "board"}) // 연관된 엔티티를 제외하여 순환 참조 방지 및 보안 강화 때문에 사용한다.
public class Reply {
// 일반적으로ID는 LONG 타입을 사용하는 것을 권장한다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
// Null 값이 들어올 수 없어 - 기본 값은 Null 허용
@Column(nullable = false)
private String comment;
// 한 사용자는 댓글을 여러개 작성할 수 있다. (지금은 댓글을 기준으로 적어야 하므로 N:1)
// 지연로딩
@ManyToOne(fetch = FetchType.LAZY)
// 단방향 관계 설계 -> User 엔티티(Entity)에는 정보가 없다.
@JoinColumn(name = "user_id")
private User user;
// 하나의 게시글에는 여러개의 댓글이 있을 수 있다. (1:N)
// 앙방향 매핑 (FK 주인은 댓글 Reply 이다)
// 지연로딩
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "board_id")
private Board board;
// JPA 엔티티(Entity)에서 데이터베이스에서 저장할 필요가 없는 필드를 정의할 때 사용한다.
// 여기서는 필요한데 DB에 저장할 내용은 아니라는 뜻
@Transient
private boolean isReplyOwner;
@Builder.Default // 위에 @Builder 패턴이 없어서 빨간줄 뜬다.
// 댓글 상태 (ACTIVE, DELETED)
private String status = "ACTIVE";
// 처음 엔티티가 생성될 때 자동으로 현재 시간으로 설정 --> db에 now() 이런거 쓸 필요가 없다.
@CreationTimestamp
@Column(name = "created_at")
private LocalDateTime createdAt;
/**
* 엔티티가 데이터베이스에 영속화 되기 전에 호출 되는 메서드가 있다면 사용한다.
* @PrePersist 어노테이션은 JPA 라이프 사이클 이벤트 중 하나로 엔티티가 영속화 되기전에 실행 된다.
*/
@PrePersist
protected void onCreate() {
if(this.status == null) {
this.status = "ACTIVE";
}
if(this.createdAt == null) {
this.createdAt = LocalDateTime.now();
}
}
}
Board.java 수정
더보기
package com.tenco.blog_v1.board;
import com.tenco.blog_v1.reply.Reply;
import com.tenco.blog_v1.user.User;
import jakarta.persistence.*;
import lombok.*;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
@Getter
@Setter
@NoArgsConstructor
@Entity
@Table(name = "board_tb")
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY) // 기본 키 전략, DB 위임
// 데이터베이스가 기본 키 값을 직접 관리하도록 위임
private Integer id;
private String title;
@Lob // 대용량 데이터 저장 가능
private String content;
@ManyToOne(fetch = FetchType.LAZY) // EAGER 즉시 전략
@JoinColumn(name = "user_id")
private User user; // 게시글 작성자 정보
// created_at 컬럼과 매핑하여, 이 필드는 데이터 저장시 자동으로 설정 됨
@Column(name = "created_at", insertable = false, updatable = false)
private Timestamp createdAt;
// 코드 추가
@Transient // 해당 테이블에 컬럼을 만들지 마 (테이블에 만들지 않음)
// 즉, JPA 메모리 상에서만 활용 가능한 필드 메서드 이다.
boolean isBoardOwner;
@OneToMany(mappedBy = "board", fetch = FetchType.LAZY)
// 댓글 엔티티를 넣어서 관계 설정하면 -- 양방향
private List<Reply> replies = new ArrayList<Reply>(); // 빠른 초기화
@Builder
public Board(Integer id, String title, String content, User user, Timestamp createdAt) {
this.id = id;
this.title = title;
this.content = content;
this.user = user;
this.createdAt = createdAt;
}
}
RDBMS 스키마 구조 확인
CREATE TABLE reply_tb (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
comment VARCHAR(255) NOT NULL,
user_id BIGINT NOT NULL,
board_id BIGINT NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES user_tb(id),
FOREIGN KEY (board_id) REFERENCES board_tb(id)
);

data.sql
샘플 데이터 추가 입력
-- 사용자 데이터 삽입
INSERT INTO user_tb(username, password, email, role, created_at) VALUES('길동', '1234', 'a@nate.com', 'USER', NOW());
INSERT INTO user_tb(username, password, email, role, created_at) VALUES('둘리', '1234', 'b@nate.com', 'USER', NOW());
INSERT INTO user_tb(username, password, email, role, created_at) VALUES('마이콜', '1234', 'c@nate.com', 'ADMIN', NOW());
-- 게시글 데이터 삽입
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('제목1', '내용1', 1, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('제목2', '내용2', 1, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('제목3', '내용3', 2, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('제목4', '내용4', 3, NOW());
-- 댓글 데이터 삽입
INSERT INTO reply_tb(comment, board_id, user_id, created_at, status) VALUES('댓글1', 4, 1, NOW(), 'DELETED');
INSERT INTO reply_tb(comment, board_id, user_id, created_at, status) VALUES('댓글1', 4, 1, NOW(), 'ACTIVE');
INSERT INTO reply_tb(comment, board_id, user_id, created_at, status) VALUES('댓글2', 4, 1, NOW(), 'DELETED');
INSERT INTO reply_tb(comment, board_id, user_id, created_at, status) VALUES('댓글3', 4, 2, NOW(), 'ACTIVE');
INSERT INTO reply_tb(comment, board_id, user_id, created_at, status) VALUES('댓글4', 3, 2, NOW(), 'ACTIVE');
http://localhost:8080/h2-console 에서 확인

'Spring boot' 카테고리의 다른 글
| 2024.10.17 Blog 프로젝트 만들기(JPA) 게시글 삭제 오류 해결 (미완성) (0) | 2024.10.17 |
|---|---|
| 2024.10.17 Blog 프로젝트 만들기(JPA) 댓글 목록 보기 (0) | 2024.10.17 |
| 2024.10.15 Blog 프로젝트 만들기(JPA) Service 레이어 만들기 (0) | 2024.10.15 |
| 2024.10.14 Blog 프로젝트 만들기(JPA) JPARepository 란? (2) | 2024.10.14 |
| 2024.10.14 Blog 프로젝트 만들기(JPA) 리팩토링 (0) | 2024.10.14 |