본문 바로가기
Spring boot/스프링 부트 3 백엔드 개발자 되기(자바편)

[5장] 데이터베이스 조작이 편해지는 ORM

by 리버🐦‍🔥 2023. 7. 25.

5.1 데이터베이스란?

    5.1.1 데이터베이스 관리자, DBMS

        - 데이터베이스를 관리하기 위한 소프트웨어(Database Management System)

        - 관계형, 객체-관계형, 도큐먼트형, 비관계형 등으로 분류.

 

        <관계형 DBMS - RDBMS>

            - 관계형 모델을 기반 (테이블 형태로 이루어진 데이터 저장소)

            - 각 행은 고유 키(ID)를 가지고 있음

            - (ex. H2, MySQL 등 ...)

더보기

❗️꼭 알아야 할 데이터베이스 용어

1. 테이블

2. 행

3. 열

4. 기본키

5. 쿼리

 

5.2 ORM이란?

    - ORM(object Relational Mapping)은 자바의 객체와 데이터를 연결하는 프로그래밍 기법.

    - 자바 언어로만 데이터베이스를 다룰 수 있게 한다.

<ORM의 장점과 단점>
[장점]
1. SQL을 직접 작성하지 않고 사용하는 언어로 데이터베이스에 접근할 수 있다.
2. 객체지향적으로 코드를 작성할 수 있기 때문에 비즈니스 로직에만 집중 가능하다.
3. 데이터베이스 시스템에 대한 종속성이 줄어든다.
4. 매핑하는 정보가 명확하기 대문에 ERD에 대한 의존도를 낮출 수 있고, 유지보수할 때 유리하다.

[단점]
1. 프로젝트의 복잡성이 커질수록 사용 난이도가 올라간다.
2. 복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한 경우가 있다.

 

5.3 JPA와 하이버네이트

    - ORM의 종류

        - 자바에서는 JPA(Java Persistence API)가 표준 -> 관계형 데이터베이스 인터페이스 (내부적으로는 JDBC API를 사용하고 있음)

        - JPA는 인터페이스이므로 실제 사용을 위해서는 ORM 프레임 워크를 추가로 사용해야 한다.

JPA : 자바 객체와 데이터베이스를 연결해 관리
하이버네이트 : JPA의 인터페이스를 구현 (내부적으로는 JDBC API를 사용)
JPA <-> 하이버네이트 <-> DB 순으로 동작

        5.3.1 엔티티 매니저

            - 엔티티(entity)

                - 데이터베이스의 테이블과 매핑되는 객체(데이터 베이스의 테이블과 직접 연결된다는 것이 특징)

            - 엔티티 매니저(entity manager)

                - 엔티티를 관리해 데이터베이스와 어플리케이션 사이에서 객체를 생성, 수정, 삭제하는 등의 역할을 함.

            - 엔티티 팩토리 (entity manager factory)

                - 엔티티 매니저를 만드는 곳.

                - 스프링 부트에서는 엔티티 매니저 팩토리를 직접 만들지 않고, 내부에서 애너테이션(@Persistence Context 또는 @Autowired) 애너테이션을 통해 엔티티 매니저를 관리함.

@PersistenceContext
EntityManager em;	// 프록시 엔티티 매니저. 필요할 때 진짜 엔티티 매니저 호출
// 스프링 부트는 기본적으로 빈을 하나만 생성해서 공유하기 때문에, 동시성 문제 발생 X
// 따라서 실제로는 엔티티 매니저가 아니지만 실제 엔티티매니저 연결하는 역할을 하는 프록시(가짜)엔티티 매니저를 사용함
// 필요할 때 데이터베이스 트랜잭션과 관련된 실제 엔티티 매니저를 호출

// 즉, 엔티티 매니저는 Spring Data JPA에서 관리하므로 직접 생성 및 관리를 할 필요가 없음.

 

    5.3.2 영속성 컨텍스트

        - 엔티티를 관리하는 가상의 공간 (이것을 사용하여 데이터베이스에서 효과적으로 데이터를 가져오고, 엔티티를 편하게 사용 가능.)

 

        <영속성 컨텍스트의 특징 4가지>

        - 1차 캐시 : DB와 애플리케이션 사이에 캐시를 가지고 있어서 빠르게 처리 가능

        - 쓰기 지연 : 쿼리를 모았다가 트랜잭션을 커밋하면 한 번에 실행

        - 변경 감지 : 1차 캐시의 엔티티와 현재 엔티티의 값을 비교해서 자동으로 DB에 반영

        - 지연 로딩 : 바로 애플리케이션에 로딩하는 것이 아니라 필요할 때 쿼리를 통해 데이터를 조회

        -> DB의 접근을 최소화하여 성능을 높이는 방법들

 

        5.3.3 엔티티의 상태

        - 분리(detached) : 영속성 컨텍스트가 관리하고 있지 않은 상태

        - 관리(managed) : 영속성 컨텍스트가 관리하고 있는 상태

        - 비영속(transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태

        - 삭제(removed) : 삭제된 상태

public class EntityManagerTest {

	@Autowired
    EntityManager em;
    
    public void example() {
    	// 1. 엔티티 매니저가 엔티티를 관리하지 않는 상태 (비영속 상태)
        Member member = new Member(1L, "이경수");
        
        // 2. 엔티티가 관리하는 상태 (관리 상태)
        em.persist(member);
        
        // 3. 엔티티 객체가 분리된 상태 (분리 상태)
        em.detach(member);
        
        // 4. 엔티티 객체가 삭제된 상태 (삭제 상태)
        em.remove(member);
    }
}

 

5.4. 스프링 데이터와 스프링 데이터 JPA

    - 스프링 데이터

        - 데이터베이스 사용 기능을 클래스 레벨에서 추상화

        - 페이징 처리, 메서드 이름으로 자동 빌드 등...

 

        5.4.1 스프링 데이터 JPA

            - 스프링 데이터의 공통적인 기능에서 JPA의 유용한 기술이 추가된 기술

// 메서드 호출로 엔티티 상태 변경 예시 코드
@PersistencContext
EntityManager em;

public void join() {
	// 기존에 엔티티 상태를 바꾸는 방법(메서드 호출을 해서 상태 변경)
    Member member = new Member(1L, "홍길동");
    em.persist(member);
}
// 기본 CRUD 메서드를 사용하기 위한 JpaRepository 상속 예
public interface MemberRepository extends JpaRepository<Member, Long> {}

 

    5.4.2 스프링 데이터 JPA에서 제공하는 메서드 사용해보기

package me.kyungsoolee.springbootdeveloper;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class MemberService {

    @Autowired
     MemberRepository memberRepository;

    public void test() {
        // 1. 생성(Create)
        memberRepository.save(new Member(1L, "A"));

        // 2. 조회(Read)
        Optional<Member> member = memberRepository.findById(1L);    // 단건 조회
        List<Member> allMembers = memberRepository.findAll();       // 전체 조회

        // 3. 삭제(Delete)
        memberRepository.deleteById(1L);
    }
}

    

5.5 예제 코드 살펴보기

package me.kyungsoolee.springbootdeveloper;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity // 1. 엔티티로 지정
/*
* @Entity(name = "member_list") // 'member_list'이라는 이름을 가진 테이블과 매핑
* */
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)  // 2. 기본 생성자
@AllArgsConstructor
public class Member {

    @Id // 3. id 필드를 기본키로 지정
    @GeneratedValue(strategy = GenerationType.IDENTITY) // 4. 기본키 자동으로 1씩 증가
    @Column(name = "id", updatable = false)
    private Long id;    // DB 테이블의 'id' 컬럼과 매칭

    @Column(name = "name", nullable = false)    // 5. name이라는 not null 컬럼과 매핑
    private String name;    // DB 테이블의 'name' 컬럼과 매칭
}

    - 접근 제어자는 public 또는 protected여야 한다.

더보기

<자동키 생성 설정 방식>

- AUTO : 선택한 데이터베이스 방언(dialect)에 따라 방식을 자동으로 선택(기본값)

- IDENTITY : 기본 키 생성을 데이터베이스에 위임(=AUTO_INCREMENT)

- SEQUENCE : 데이터베이스 시퀀스를 사용해서 기본 키를 할당하는 방법. 오라클에서 주로 사용

- TABLE : 키 생성 테이블 사용

 

<@Column 애너테이션의 속성>

- name : 필드와 매핑할 컬럼 이름. 설정하지 않으면 필드 이름으로 지정

- nullable : 컬럼의 null 허용 여부. 설정하지 않으면 true(nullable)

- unique : 컬럼의 유일한 값(unique)여부. 설정하지 않으면 false(non unique)

- columnDefinition : 컬럼 정보 설정. default값을 줄 수도 있음

<궁금한 내용>
사용자 <-> 리포지토리 <-> DB <-> 엔티티?....

리포지토리랑 엔티티?...의 관계?... 잘 모르겠음...
구조에 대한 생각을 좀 더 적립하고 가야될 듯.
<5장 핵심 요약>
1. ORM : 객체와 데이터베이스를 연결하는 프로그래밍 기법
2. JPA : 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
3. 하이버네이트 : JPA의 구현체 중 대표적인 구현체. 자바를 위한 ORM 프레임
4. 스프링 데이터 JPA : JPA를 쓰기 편하게 만들어 놓은 모듈