1-6 데이터베이스 테이블 간의 객체 간의 관계
릴레이션 오브젝트 = ORM (Object-Relational Mapping),
FetchType.LAZY와 FetchType.EAGER
Aug 20, 2024
1. 릴레이션 오브젝트 = ORM (Object-Relational Mapping)
"릴레이션 오브젝트"라는 용어는 "관계형 객체" 또는 "릴레이션 객체"로 해석될 수 있으며, 주로 데이터베이스와 객체 지향 프로그래밍 사이의 관계를 설명할 때 사용됩니다. 이 개념은 데이터베이스 테이블 간의 관계와 객체 간의 관계를 연결짓는 것을 의미합니다. 가장 일반적으로는 **ORM(Object-Relational Mapping)**과 관련된 용어로 사용됩니다.
주요 개념:
- ORM (Object-Relational Mapping):
- 객체 지향 프로그래밍 언어에서 데이터베이스 테이블을 객체로 매핑하는 기술입니다. 예를 들어, 데이터베이스의 테이블을 클래스와 매핑하고, 테이블의 레코드를 객체로 다루게 됩니다.
- Entity: 데이터베이스 테이블에 대응하는 클래스입니다.
- Relationship: 엔티티 간의 관계로, 데이터베이스 테이블 간의 관계(1:1, 1, N)가 클래스 간의 관계로 표현됩니다.
- Entity Relationship:
- 1:1 관계: 두 테이블이 1:1 관계를 가지며, 각각의 레코드가 서로 하나의 레코드에만 연결됩니다. 예를 들어,
User
와Profile
클래스가 있을 때, 각각의User
는 하나의Profile
만 가질 수 있습니다. - 1관계: 한 테이블의 한 레코드가 다른 테이블의 여러 레코드와 연결되는 관계입니다. 예를 들어,
User
와Order
클래스가 있을 때, 한User
는 여러Order
를 가질 수 있습니다. - N관계: 두 테이블 간에 여러 레코드가 서로 연결될 수 있는 관계입니다. 이를 표현하기 위해 보통 연결 테이블이 사용됩니다. 예를 들어,
Student
와Course
가 N관계일 때, 각 학생은 여러 수업을 듣고, 각 수업은 여러 학생이 들을 수 있습니다.
- 예시 (Java & JPA):
- Entity 클래스 정의:
- Database Table:
User
테이블은Profile
과 1:1 관계,Order
와 1관계를 가집니다.Profile
테이블은User
와 1:1 관계를 가집니다.Order
테이블은User
와 N:1 관계를 가집니다.
java코드 복사
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
private Profile profile; // 1:1 관계
@OneToMany(mappedBy = "user")
private List<Order> orders = new ArrayList<>(); // 1:N 관계
}
@Entity
public class Profile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String bio;
}
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String product;
@ManyToOne
@JoinColumn(name = "user_id")
private User user; // N:1 관계
}
정리:
"릴레이션 오브젝트"는 객체 지향 프로그래밍에서 데이터베이스 테이블 간의 관계를 객체로 표현한 개념입니다. ORM 프레임워크는 이러한 관계를 쉽게 다룰 수 있도록 도와주며, 엔티티 간의 관계를 정의하고 관리하는 역할을 합니다. 이를 통해 개발자는 데이터베이스의 복잡한 관계를 객체 지향적으로 쉽게 다룰 수 있게 됩니다.
Board
package shop.mtcoding.blog.board;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import shop.mtcoding.blog.user.User;
import java.sql.Timestamp;
@NoArgsConstructor // 빈 생성자 (하이버네이트가 om 할때 필요)
@Setter
@Getter
@Table(name = "board_tb")
@Entity // DB에서 조회하면 자동 매핑이됨
public class Board {
@GeneratedValue(strategy = GenerationType.IDENTITY) // auto_incremnt 설정, 시퀀스 설정
@Id // PK 설정
private Integer id;
@Column(nullable = false)
private String title;
@Column(nullable = false)
private String content;
private Timestamp createdAt;
//fk N한테 FK를 만들어야한다.
//User table 에 select 를 한 번더 돌린다.
@ManyToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.EAGER)
private User user;
@Builder
public Board(Integer id, String title, String content, Timestamp createdAt) {
this.id = id;
this.title = title;
this.content = content;
this.createdAt = createdAt;
}
}
@ManyToOne(fetch = FetchType.LAZY)
와 @ManyToOne(fetch = FetchType.EAGER)
는 JPA에서 엔티티 간의 다대일 관계를 정의할 때 사용하는 애노테이션으로, 이 두 가지는 연관된 엔티티를 로딩하는 방식에 차이가 있습니다.FetchType.LAZY
와 FetchType.EAGER
비교
- 지연 로딩 (
FetchType.LAZY
) - 설명: 연관된 엔티티를 실제로 필요할 때까지 로드하지 않습니다. 즉,
User
엔티티가 참조될 때까지 데이터베이스에서User
엔티티를 가져오지 않습니다. - 동작 방식:
- 처음에 주 엔티티를 조회할 때
User
엔티티는 로드되지 않습니다. User
엔티티가 실제로 접근될 때, 그때 비로소 데이터베이스에select
쿼리가 실행되어User
데이터를 가져옵니다.- 장점: 메모리 사용량이 줄고, 불필요한 데이터를 미리 로드하지 않으므로 성능이 최적화될 수 있습니다.
- 단점:
User
엔티티를 사용할 때마다 추가적인 데이터베이스 쿼리가 발생할 수 있습니다.
- 즉시 로딩 (
FetchType.EAGER
) - 설명: 연관된 엔티티를 주 엔티티와 함께 즉시 로드합니다. 즉,
User
엔티티를 주 엔티티와 동시에 데이터베이스에서 가져옵니다. - 동작 방식:
- 주 엔티티를 조회할 때,
User
엔티티도 즉시 함께 조회됩니다. - 데이터베이스 쿼리는 주 엔티티와
User
엔티티를 함께 조회하여 한 번에 데이터를 가져옵니다. - 장점: 주 엔티티와 연관된
User
엔티티가 항상 필요할 때 유용하며, 추가적인 데이터베이스 접근이 없어 성능이 최적화될 수 있습니다. - 단점: 불필요한 데이터가 미리 로드될 수 있어 메모리 사용량이 증가할 수 있으며, 경우에 따라 성능이 저하될 수 있습니다. 특히 연관된 데이터가 많을 때 문제가 될 수 있습니다.
- *
FetchType.LAZY
*를 사용하는 경우: User
엔티티가 항상 필요한 것이 아니라 특정 상황에서만 필요할 때.- 불필요한 데이터 로딩을 피하고 메모리 사용을 최소화하고자 할 때.
- 연관된 데이터가 크거나, 많은 엔티티와의 관계가 있는 경우.
- *
FetchType.EAGER
*를 사용하는 경우: - 주 엔티티와 연관된
User
엔티티가 항상 필요한 경우. - 애플리케이션의 로직에서
User
엔티티를 자주 사용하는 경우. - 추가적인 데이터베이스 쿼리를 피하고, 처음부터 모든 데이터를 가져오는 것이 더 효율적일 때.
사용 시기 비교
2.캐싱
캐싱은 데이터나 객체를 미리 저장해두고, 이후에 필요할 때 빠르게 접근할 수 있도록 하는 기법입니다. 이를 통해 데이터베이스 조회나 계산이 반복될 때 성능을 크게 향상시킬 수 있습니다.
@ManyToOne
관계에서 캐싱을 사용하는 것은, 특히 fetch = FetchType.LAZY
가 사용될 때 유용할 수 있습니다. 지연 로딩 시 매번 데이터베이스에 접근하지 않고, 한 번 로드한 데이터를 캐시에 저장해 두었다가 다시 사용할 수 있기 때문입니다.JPA에서 캐싱을 사용하는 방법은 크게 1차 캐시와 2차 캐시로 나눌 수 있습니다:
- 1차 캐시:
- JPA에서 기본적으로 제공하는 캐시로, 동일한 트랜잭션 내에서 동일한 엔티티를 다시 조회할 때 데이터베이스에 접근하지 않고 메모리에서 데이터를 가져옵니다.
- 1차 캐시는
EntityManager
수준에서 관리되며, 트랜잭션이 끝나면 캐시가 사라집니다.
- 2차 캐시:
- 애플리케이션 전역에서 엔티티를 캐싱하는 방법입니다. 2차 캐시는 애플리케이션이 종료되거나, 명시적으로 캐시를 지우기 전까지 유지됩니다.
- 2차 캐시는 Hibernate에서 지원하며, Ehcache, Infinispan, Hazelcast 같은 외부 캐시 라이브러리와 통합하여 사용할 수 있습니다.
- 이를 설정하면, 특정 엔티티가 한 번 데이터베이스에서 조회된 후 다른 트랜잭션이나 요청에서 동일한 엔티티가 필요할 때마다 데이터베이스를 다시 조회하지 않고 캐시된 데이터를 사용하게 됩니다.
캐싱을 적절히 사용하면, 데이터베이스 부하를 줄이고 애플리케이션의 응답 속도를 크게 향상시킬 수 있습니다. 하지만 캐시의 일관성을 유지하는 것과 캐시가 갱신될 때 발생할 수 있는 문제를 잘 관리하는 것이 중요합니다.

강사님께서 알려주신 "N포드"의 의미와 용어 해석
N
관계를 다루는 조인 방식에서의 "포드"를 지칭하는 것으로 보입니다. 여기서 "포드"는 다음과 같은 의미를 가진다.
- "N": 두 테이블 간의 관계에서 'N'은 여러 개의 레코드를 의미합니다. 즉, 두 테이블 간의 조인에서 각각 여러 개의 관련된 레코드가 있을 수 있음을 나타냅니다.
- "포드": 이 부분은 "Join" 또는 **"Join Table"**과 관련된 개념으로, 여러 개의 레코드가 서로 연결될 수 있는 관계를 설명합니다.
N 관계 및 드라이빙 테이블
N관계는 다대다 관계를 의미하며, 두 테이블 간의 조인에서 많은 레코드가 서로 연결될 수 있는 관계입니다. 이 관계를 표현하기 위해
연결 테이블 (중간 테이블)을 사용합니다.
예시
- 학생(Student)과 수업(Course):
- 학생은 여러 수업을 들을 수 있습니다.
- 수업은 여러 학생이 수강할 수 있습니다.
- 이 경우, 연결 테이블을 사용하여 이들 간의 관계를 정의합니다.
sql코드 복사
-- 연결 테이블 예시
CREATE TABLE Student_Course (
student_id INT,
course_id INT,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES Student(id),
FOREIGN KEY (course_id) REFERENCES Course(id)
);
드라이빙 테이블
드라이빙 테이블은 SQL 쿼리에서 조인을 수행할 때 쿼리 최적화를 위해 선택된 첫 번째 테이블입니다. 드라이빙 테이블은 일반적으로 데이터 양이 적거나 인덱스가 잘 설정된 테이블로 선택됩니다.
종합 설명
- N: 두 테이블 간의 관계에서 많은 레코드를 의미합니다.
- 포드: 조인 또는 연결을 다루는 테이블로, N관계를 나타내는 연결 테이블을 의미합니다.
따라서, "N포드"는"N관계"*의 개념을 설명하기 위해 사용된 용어이다. 이를 통해 두 테이블 간의 다대다 관계를 표현하고, 관련된 데이터를 조회하는 방식과 그에 따른 드라이빙 테이블 선택의 중요성을 나타낼 수 있습니다.
Share article