1. 객체 관계 매핑
- 객체와 테이블을 자동으로 매핑해주는 기술
- 객체지향언어에서의 클래스를, RDBMS의 테이블을 사용하므로 불일치가 존재
- ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성해 불일치 해결
장점
1. 객체 지향적 코드로 인해 직관적
2. 비즈니스 로직에 집중할 수 있도록 지원
3. DBMS에 대한 종속성이 줄어듦
4. 편리성
단점
1. DBMS 고유의 기능을 사용하기 어려움
2. 프로젝트가 커질수록 난이도 상승
3. 잘못 구현 시 심각한 속도 저하와 일관성을 무너뜨리는 현상 발생
1(Driven) : N(F, D) Driving Table → 엔포드
→ 한 명이 여러 글 작성가능
N : N 이면 중간에 동사 테이블 나옴
DB에서 조회 시 id, title, content, createdAt, user 조회
SELECT * FROM board_tb
→ Hivernate를 통해 ORM 시작
빈 생성자를 때려서 new Board.
board 안에 id, title, content, createdAt
Where 문으로 특정 ID 조회 후 그 결과를 기존 테이블(board_tb)에 적용 → ORM
2. Loading
JPA에서 연관관계를 조회할 때 참조하는 객체들의 조회 시점을 선택할 수 있도록 제공하는
두 가지 로딩 방법
Board Entity
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
@ManyToOne(fetch = FetchType.EAGER) // N(board) : 1(user) , EAGER = 1? orm 해줘야지
// @ManyToOne(fetch = FetchType.LAZY)
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;
}
}
User Entity
package shop.mtcoding.blog.user;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.sql.Timestamp;
@Getter
@Setter
@Table(name = "user_tb")
@NoArgsConstructor
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(unique = true, nullable = false)
private String username; // 아이디
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
private Timestamp createdAt;
@Builder
public User(Integer id, String username, String password, String email, Timestamp createdAt) {
this.id = id;
this.username = username;
this.password = password;
this.email = email;
this.createdAt = createdAt;
}
}
Dummy
insert into user_tb(username, password, email, created_at) values('shin', '1234', 'shin@naver.com', now());
insert into user_tb(username, password, email, created_at) values('kim', '1234', 'kim@naver.com', now());
insert into user_tb(username, password, email, created_at) values('lee', '1234', 'lee@naver.com', now());
insert into board_tb(title, content, created_at, user_id)
values ('제목1', '내용1', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목2', '내용2', now(), 1);
insert into board_tb(title, content, created_at, user_id)
values ('제목3', '내용3', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목4', '내용4', now(), 2);
insert into board_tb(title, content, created_at, user_id)
values ('제목5', '내용5', now(), 2);
Board - User = N : 1 매핑 관계
2-1. Eager Loading (즉시 로딩)
Entity 조회 시 자신과 관련된 Entity를 Join을 통해 함께 조회하는 방식
- Entity A 조회시 관련된 Entit B를 같이 가져옴. (실제 Entity Mapping) Join을 사용하여 한 번에 가져옴
- A Join B, 쿼리가 한 번만 나감
- Join으로 인한 성능 저하 및 N + 1 문제를 일으킴 → 불필요한 Join까지 포함해 처리해서 Lazy Loading 사용 권장
TEST 코드
@Test
public void findAll_test() {
// given
// when
System.out.println("1. 첫번째 조회");
List<Board> boardList = boardRepository.findAll();
System.out.println("userId : "+boardList.get(0).getUser().getId());
System.out.println("==========================");
// eye
System.out.println("2. Eager Loading");
System.out.println("title : "+boardList.get(0).getUser().getUsername());
System.out.println("title : "+boardList.get(1).getUser().getUsername());
System.out.println("title : "+boardList.get(2).getUser().getUsername());
}
결과

2-2. Lazy Loading
자신과 관련된 Entity를 실제로 사용할 때 관련된 Entity를 조회(SELECT) 하는 방식
- Entity A를 조회시 관련(Reference)된 Entity B를 한 번에 가져오지 않음. → proxy를 mapping하고 실제 B 조회시 쿼리가 나감
- Query가 두 번 나감 (A 조회 한 번, B 조회 한 번)
TEST 코드
@Test
public void findAll_test() {
// given
// when
System.out.println("1. 첫번째 조회");
List<Board> boardList = boardRepository.findAll();
System.out.println("userId : "+boardList.get(0).getUser().getId());
System.out.println("==========================");
// eye
System.out.println("2. Lazy Loading");
System.out.println("title : "+boardList.get(0).getUser().getUsername());
System.out.println("title : "+boardList.get(1).getUser().getUsername());
System.out.println("title : "+boardList.get(2).getUser().getUsername());
} // DEC로 인해 0 = 5번 게시글
결과

- Board → User(FK) Lazy 전략 순수하게 Board Select하면 User는 Select x
- OpenInView = true C - S - R (o) (o) (o)
- OpenInView = false C - S - R (x) (o) (o)
Share article