gradle 확인
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.10'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
application.yml
spring:
application:
name: demo
server:
port: 8080
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
hibernate:
ddl-auto: create # 하이버네이트의 DDL 자동 실행 옵션 설정
h2:
console:
enabled: true # H2 콘솔 웹 인터페이스 활성화
path: /h2-console # H2 콘솔에 접근할 수 있는 경로 설정
# 디버깅을 위한 로깅 설정
logging:
level:
org:
springframework:
jdbc: DEBUG
hibernate:
SQL: DEBUG
JPA 사용해 보기
package com.example.demo.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
@Entity
public class Address {
// @Id: 이 필드는 데이터베이스 테이블의 기본 키(primary key)를 나타냅니다.
// 이 어노테이션은 해당 필드가 엔티티의 고유 식별자임을 나타내며,
// 데이터베이스 테이블에서 이 필드를 기준으로 레코드를 구분합니다.
// @GeneratedValue(strategy = GenerationType.IDENTITY):
// 이 어노테이션은 기본 키가 자동으로 생성되도록 설정합니다.
// GenerationType.IDENTITY 전략은 데이터베이스의 "AUTO_INCREMENT" 기능을 사용하여
// 기본 키 값을 자동으로 생성합니다.
// 따라서, 새로운 엔티티가 데이터베이스에 삽입될 때마다
// 고유한 ID 값이 자동으로 생성되고 할당됩니다.
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String city;
private String street;
}
매우 중요!!
참조 방향성(FK 에 주인은 누구)
- 단방향 매핑 (Unidirectional Mapping): 한쪽에서만 참조하는 관계.
- 양방향 매핑 (Bidirectional Mapping): 양쪽에서 서로 참조하는 관계.
엔티티 간 관계
- 일대일 매핑 (One-to-One Mapping): 하나의 엔티티가 다른 하나의 엔티티와 연결되는 관계.
- 일대다 매핑 (One-to-Many Mapping): 한 엔티티가 여러 엔티티와 연결되는 관계.
- 다대일 매핑 (Many-to-One Mapping): 여러 엔티티가 하나의 엔티티와 연결되는 관계.
- 다대다 매핑 (Many-to-Many Mapping): 두 엔티티가 서로 여러 개의 다른 엔티티들과 연결되는 관계.
User와 Address 사이의 One-to-One 관계를 SQL로 표현할 때, 외래 키(FK)는 User 테이블이 Address 테이블을 참조하는 방식으로 설정됩니다. 즉, User 테이블이 Address 테이블의 기본 키(PK)를 외래 키로 참조하게 됩니다. 이로 인해 User는 특정 Address와 연결되고, Address는 User에 대해 아무런 정보를 가지지 않게 됩니다.
**시나리오 코드 2
Post, Comment 엔티티 만들어 보기**
자바의 객체 지향적인 관점에서 관계 살펴 보기
Post 객체는 여러 개의 Comment 객체를 참조할 수 있습니다. 이는 List<Comment>를 통해 구현되며, 이를 통해 Post 객체는 자신과 연결된 여러 개의 댓글을 관리합니다. 반대로, Comment 객체는 각각 하나의 Post 객체를 참조합니다. 즉, 댓글은 항상 하나의 게시글에 속하게 됩니다.
SQL 관점에서 관계 살펴보기
Post와 Comment의 관계는 SQL 데이터베이스에서 **1:N (일대다)**관계로 정의됩니다. 즉, 하나의 게시글(Post)은 여러 개의 댓글(Comment)과 연결될 수 있습니다.
1. Post 테이블
Post 테이블은 각 게시글에 대한 정보를 담고 있으며, 각 게시글은 고유한 ID로 식별됩니다. SQL에서 이를 나타내기 위해 기본 키(PK)를 설정합니다.
CREATE TABLE Post (
id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 게시글의 고유 ID (PK)
title VARCHAR(255), -- 게시글 제목
content TEXT -- 게시글 내용
);
2. Comment 테이블
Comment 테이블은 각 댓글에 대한 정보를 담고 있으며, 각 댓글은 고유한 ID로 식별됩니다. 댓글은 어느 게시글에 속하는지를 나타내기 위해 **외래 키(FK)**를 사용합니다. 이 외래 키(FK)는 Post 테이블의 ID를 참조하며, 이를 통해 댓글이 어느 게시글에 속하는지 데이터베이스에서 관리됩니다.
CREATE TABLE Comment (
id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 댓글의 고유 ID (PK)
text TEXT, -- 댓글 내용
post_id BIGINT, -- 어느 게시글에 속하는지 참조 (FK)
FOREIGN KEY (post_id) REFERENCES Post(id) -- Post 테이블의 id를 참조 (FK)
);
**참조의 방향성
Post**와 Comment 사이에서 참조의 방향성을 단방향 또는 양방향으로 설정할 수 있으며, 두 방식은 각각의 장단점이 있습니다. 실무에서는 보통 위와 같은 경우 양방향 참조를 많이 선택할 수 있지만 단방향성으로 만들더라도 잘못된 부분은 없습니다.
1. 단방향 참조
장점:
- 구현이 단순:
- 한쪽에서만 참조하기 때문에 설계가 간단합니다. 필요하지 않은 방향으로의 참조를 구현할 필요가 없어서 코드가 더 단순해집니다.
- 성능 최적화:
- 불필요한 참조를 하지 않으므로 메모리 사용을 줄일 수 있으며, 필요 없는 객체를 로드하지 않아도 됩니다. 특히 지연 로딩(Lazy Loading) 전략과 결합하면 성능이 최적화될 수 있습니다.
- 메모리 사용 절약:
- 단방향으로만 참조할 경우, 반대 방향으로의 객체를 유지할 필요가 없기 때문에 메모리 사용을 줄일 수 있습니다.
단점:
- 양방향 탐색 불가:
- 한쪽에서만 참조하기 때문에, 참조되지 않는 방향에서는 해당 객체를 참조하거나 조회할 수 없습니다. 예를 들어, Comment에서 Post를 참조할 수 없으므로, Comment에서 속한 Post에 접근할 수 없습니다.
- 관계 복잡성이 증가:
- 만약 반대쪽에서 객체를 참조해야 할 경우, 별도의 쿼리를 작성해야 하며, 이로 인해 코드 복잡성이 증가할 수 있습니다.
2. 양방향 참조 (Bidirectional Relationship)
장점:
- 양방향 탐색 가능:
- 양방향 참조를 통해, Post와 Comment 모두 서로를 참조할 수 있습니다. 예를 들어, Post에서 Comment 리스트를 참조할 수 있고, Comment에서 자신이 속한 Post를 참조할 수 있어 관계 탐색이 매우 용이합니다.
- 더 직관적인 객체 탐색:
- 양쪽에서 데이터를 쉽게 탐색할 수 있기 때문에, 개발자가 두 엔티티 간의 관계를 더 쉽게 관리하고 탐색할 수 있습니다. 이로 인해 코드 가독성이 좋아지고 유지보수가 쉬워집니다.
- 더 적은 쿼리:
- 데이터베이스에서 한쪽을 조회할 때 반대쪽 객체도 쉽게 접근할 수 있어, 필요에 따라 추가 쿼리 작성이 줄어들 수 있습니다.
단점:
- 설계의 복잡성 증가:
- 양방향 참조에서는 어느 엔티티가 연관관계의 주인인지를 명확히 설정해야 하고, 이로 인해 설계가 복잡해질 수 있습니다. 특히 mappedBy와 같은 설정을 올바르게 사용해야 합니다.
- 메모리 사용 증가:
- 양방향으로 참조를 유지하기 때문에, 두 엔티티가 서로를 참조할 경우 메모리 사용량이 늘어날 수 있습니다.
- 순환 참조의 위험:
- 양쪽에서 서로를 참조하면서 순환 참조(Circular Reference)가 발생할 수 있습니다. 이는 직렬화(serialization)나 JSON 변환에서 무한 루프를 일으킬 수 있습니다. 이런 문제를 해결하기 위해 @JsonIgnore와 같은 어노테이션을 사용해야 할 때도 있습니다.
package com.example.demo.model;
import java.util.List;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity(name = "tb_comment")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Comment {
@Id // PK 지정
@GeneratedValue(strategy = GenerationType.IDENTITY) // 코드 --> db 위임
private Long id;
private String text;
@ManyToOne
@JoinColumn(name = "post_id")
private Post post;
}
package com.example.demo.model;
import java.util.List;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity(name = "tb_post")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String content;
// mappedBy : post - 연관 관계의 주인이 Comment 엔티티에 post(속성) 필드 임을 나타냅니다.
// 객체 필드 기준으로 생각해야 합니다.
// CascadeType.ALL - 제약을 설정하게 되면 Post 엔티티에 대한 모든 상태 변경(저장, 삭제 등)이
// 관련된 Commnet 엔티티에 전파 된다.
@OneToMany(mappedBy = "post" , cascade = CascadeType.ALL)
private List<Comment> comments;
}
'Spring boot > 개념 공부' 카테고리의 다른 글
블로그 엔티티 만들기 - 2 (0) | 2024.10.01 |
---|---|
블로그 프로젝트 만들기 - 1 (6) | 2024.10.01 |
영속성 컨텍스트란? (0) | 2024.10.01 |
엔티티 매니저에 대해 알아보자. (1) | 2024.10.01 |
JPA와 하이버네이트란? (0) | 2024.10.01 |