ORM (객체 관계 매핑)

SHIN's avatar
Aug 22, 2024
ORM (객체 관계 매핑)

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()); }

결과

notion image
 
 

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번 게시글
 

결과

notion image
 
 
 

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

SHIN