본문 바로가기
개인공부/스프링 부트 3 백엔드 개발자 되기

스프링 부트 - 5장_데이터베이스 조작이 편해지는 ORM

by 응가1414 2023. 6. 29.

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

  1. SQL을 몰라도 데이터베이스를 조작할 수 있게 해주는 편리한 도구인 ORM을 공부하겠습니다.
  2. ORM은 SQL공부를 한 적이 있다면 그 편리함을 더 크게 느낄수 있는 도구인데요
  3. 스프링 부트의 ORM 기술 표준인 스프링 JPA와 이를 구현하기 위한 하이버네이트를 사용합니다.
 ORM은 "Object-Relational Mapping"의 약어입니다. ORM은 객체 지향 프로그래밍 언어와 관계형 데이터베이스 간의 데이터 변환과 상호작용을 자동화하기 위해 사용되는 기술입니다.
 ​
 JPA는 "Java Persistence API"의 약어입니다. JPA는 자바에서 ORM을 구현하기 위한 표준 인터페이스를 제공하는 API입니다. JPA를 사용하여 객체와 데이터베이스 간의 매핑과 데이터베이스 작업을 관리할 수 있습니다.
 ​
 요약:
 ORM은 객체 지향 프로그래밍 언어와 관계형 데이터베이스 간의 데이터 변환과 상호작용을 자동화하기 위한 기술이며, 
 JPA는 자바에서 ORM을 구현하기 위한 표준 인터페이스를 제공하는 API입니다.
 ​
 하이버네이트는 자바 기반의 ORM(객체-관계 매핑) 프레임워크로, 객체와 관계형 데이터베이스 간의 데이터 변환과 상호작용을 자동화하여 개발자의 생산성을 향상시키는 역할을 합니다.

핵심 키워드

  1. DBMS
  2. ORM
  3. JPA
  4. 하이버네이트
  5. 엔티티
  6. 영속성 컨텍스트
  7. 스프링 테이터 JPA

데이터베이스란?

  1. 데이터베이스는 데이터를 매우 효율적으로 보관하고 꺼내 볼 수 있는 곳
  2. 데이터베이스를 사용하면 얻을 수 있는 가장 큰 이점은 굉장히 많은 사람이 안전하게 데이터를 사용하고
    1. 관리할수 있다는 것이다.

데이터베이스 관리자, DBMS (Database Management System)

  1. 데이터베이스를 관리하기 위한 소프트웨어를 DBMS라고 한다.
  2. 데이터베이스는 많은 사람이 공유할 수 있어야 하므로 동시 접근을 할 수 있어야 한다.
  3. DBMS는 이런 요구사항을 만족하면서도 효율적으로 데이터베이스를 관리하고 운영한다.

관계형 DBMS (Relational DBMS)

  1. 관계형 DBMS(relational DBMS)라고 부른다.
    1. 관계형이라는 말을 쓰는 이유는 이 DBMS가 관계형 모델을 기반으로 하기 때문이다.
  2. RDBMS는 테이블 형태로 이루어진 데이터 저장소를 생각하면 된다.

ID이메일LIOI

1 a@test.com 10
2 b@test.com 20
3 c@test.com 30
  • 1, a@test.com, 10 한 줄을 행이라고 하고
  • ID, 이메일 나이와 같은 구분을 열이라고 한다.

H2, MySQL

  1. 이 책에서 사용할 RDMBS는 H2, MySQL이다.
  2. H2는 자바로 작성되어 있는 RDBMS이다.
    1. 스프링 부트가 지원하는 인메모리 관계형 데이터베이스
    2. H2는 데이터를 다른 공간에 따로 보관하는 것이 아니라
    3. 애플리케이션 자체 내부에 데이터를 저장하는 특징이 있다.
    4. 애플리케이션을 다시 실행하면 데이터는 초기화가 된다.
      1. 하지만 간편하게 사용하기 좋아서 개발 시에는 테스트 용도로 많이 사용한다.
  3. 개발 시에는 H2를, 실제 서비스로 올릴 때는 MySQL

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

  • 테이블 table
    • 테이블은 데이터베이스에서 데이터를 구성하기 위한 가장 기본적인 단위입니다.
    • 테이블은 행과 열로 구성되며 행은 여러 속성으로 구성
  • 행 row
    • 행은 테이블의 구성 요소 중 하나이며 테이블의 가로로 배열된 데이터의 집합을 의미한다.
    • 행은 반드시 고유한 식별자인 기본 키를 가진다.
    • 행을 레코드(record), 튜플 라고 부른다
    • 열(column)은 테이블의 구성 요소 중 하나이며
    • 행에 저장되는 유형의 데이터입니다.
    • 열은 각 요소에 대한 속성을 나타내며 무결성을 보장한다.
    • 데이터에 대한 무결성을 보장합니다. - 무결성 (완결정, 정확성, 일관성 유지)
  • 기본키 - 행구분,
    • 기본키(primary key)는 행을 구분할 수 있는 식별자
      • 이 값은 테이블에서 유일해야 하며 중복 값을 가진다.
      • 또한 기본키의 값은 수정되어서는 안되며 유효한 값이 어야 한다.
      • 즉 NULL이 될수없다.
  • 쿼리
    • 쿼리(query)는 데이터베이스에서 데이터를 조회하거나 삭제, 생성, 수정 같은 처리를 하기 위해 사용
    • SQL이라는 데이터베이스 전용 언어를 사용하여 작성

ORM이란 (Object-relational mapping)

  1. ORM(object-relational mapping)은 자바의 객체와 데이터베이스를 연결하는 프로그래밍 기법
  2. SQL은 힘들다 하지만 ORM이 있다면 데이터베이스의 값을 마치 객체처럼 사용할 수 있다.
  3. 즉, 객체와 데이터베이스를 연결해 자바 언어로만 데이터베이스를 다룰 수 있게 하는 도구를
    1. ORM이라 한다.
  4. ORM은 장점과 단점이 있다.
 장점
 1. SQL을 직접 작성하지 않고 사용하는 언어로 데이터베이스에 접근할 수 있다.
 2. 객체지향적으로 코드를 작성할 수 있기 때문에 비즈니스로 로직에만 집중할 수 있다.
 3. 데이터베이스 시스템이 추상화되어 있기 때문에 MySQL에서 PostgreSQL로 전환한다고 해도
    추가로 드는 작업이 거의 없다, 즉, 데이터베이스 시스템에 대한 중속성이 줄어든다.
 4. 매핑하는 정보가 명확하기 때문에 ERD(Entity Relationship Diagram, 객체-관계 다이어그램)에 
    대한 의존도를 낮출 수 있고 유지보수할 때 유리합니다. 
    
 단점
 1. 프로젝트의 복잡성이 커질수록 사용 난이도도 올라갑니다.
 2. 복잡하고 무거운 쿼리는 ORM으로 해결이 불가능한 경우가 있다.

JPA와 하이버네이트

  1. JPA(java persistence API)를 표준으로 사용합니다.
  2. JPA는 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
  3. JPA는 인터페이스이므로 ORM 프레임워크를 추가로 선택해야한다.

대표적으로 하이버네이트를(hibernate)를 많이 사용한다.

  1. 하이버네이트는 JPA 인터페이스를 구현한 구현체이자 자바용 ORM 프레임 워크입니다.
  2. 하이버네이트의 목표는 자바 객체를 통해 데이터베이스 종류에 상관없이 데이터베이스를 자유자재로 사용할 수 있게 한다.
 JPA와 하이버네이트의 역할
 - JPA(Java Persistence API) : 자바 객체와 데이터베이스를 연결해 데이터를 관리합니다. 
                               객체 지향 도메인 모델과 데이터베이스의 다리 역할을 합니다.
                               
 - 하이버네이트(Hibernate) : JPA의 인터페이스를 구현합니다. 내부적으로는 JDBC API를 사용합니다.

 

엔티티 매니저란

  1. JPA(Java Persistence API)의 중요한 컨셉 중 하나인 엔티티 매니저영속성 컨텍스트을 보자
  2. JPA의 중요한 컨셉 -> 엔티티 매니저, 영속성 컨텍스트

엔티티 (객체 + (DB에 영향을 미치는 쿼리))

  1. 엔티티(entity)는 데이터베이스의 테이블과 매핑되는 객체를 의미합니다.
    1. 엔티티는 본질적으로 자바 객체이므로 일반 객체와 다르지 않다.
    2. 하지만 데이터베이스의 테이블과 직접 연결된다는 아주 특별한 특징이 있어 구분지어 부른다.
  2. 즉, 엔티티는 객체이긴 하지만 테이터베이스에 영향을 미치는 쿼리를 실행하는 객체이다.

앤티티 매니저

  1. 엔티티 매니저(entity manager)는 엔티티를 관리해 데이터베이스와 어플리케이션 사이에서
    1. 객체를 생성, 수정, 삭제하는 역할을 한다.
  2. 엔티티 매니저를 만드는 곳은 엔티티 매니저 팩토리 (entity manager factory)입니다.
  • 회원 2명이 동시에 회원 가입을 하면
  • 회원 1의 요청에 대해서 가입 처리를 할 엔티티 매니저를 엔티티 매니저 팩토리가 생성하면
  • 이를 통해 가입 처리해 데이터베이스에 회원 정보를 저장하는 것이다.
    • 회원 2도 동일
    •  
    •  회원 1 회원가입 -> 엔티티 팩토리 -> 엔티티 매니저 생성 -> 커낵션 -> db 저장

(사진 112)

스프링 부트 내부에서 엔티티 매니저

스프링의 엔티티 매니저는 Spring Data JPA에서 관리하므로 직접 생성, 관리할 필요가 없다.

  1. 스프링 부트는 내부에서 엔티티 매니저 팩토리를 하나만 생성해서 관리하고
  2. @PersistenceContext Ehsms @Autowired 애너테이션을 사용해서 엔티티 매니저를 사용
 @PersistenceContext or @Autowired
 EntityManager em; // 프록시 엔티티 매니저. 필요할 때 진짜 엔티티 매니저 호출
  1. 기본적으로 빈은 하나만 생성해서 공유하므로 동시성 문제가 발생
    1. 그래서 실제로는 엔티티 매니저가 아닌 실제 엔티티 매니저와 연결하는 프록시(가짜) 엔티티 메니저를 사용
  2. 필요할 때 데이터베이스 트랜잭션과 관련된 실제 엔티티 매니저를 호출

영속성 컨텍스트

  1. 엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다는 특징이 있다
  2. 영속성 컨텍스트는 JPA의 중요한 특징 중 하나. 엔티티를 관리하는 가상의 공간입니다.
    1. 데이터 베이스에서 효과적으로 데이터를 가져올 수 있고, 엔티티를 편하게 사용할 수 있는 것이다.
  3. 영속성 컨텍스트에는 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩이라는 특징이 있다.

영속성 컨텍스트의 특징

스프링 부트에서는 이런 쿼리를 자바 코드로 작성하고 이를 JPA가 알아서 쿼리로 변경해주는 것이 매우 편리하다.

JPA의 영속성 컨텍스트를 몰라도 괜찮지만 혹시 모르니 기본적인 특징을 보자

1. 영속성 컨텍스트의 특징 - 1차 캐시

빠른 초치를 위해 Cash 사용

  1. 영속성 컨텍스트는 내부에 1차 캐시를 가지고 있다.
  2. 이때 캐시의 키는 엔티티의 @Id 애너테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티입니다.
  3. 엔티티를 조회하면 1차 캐시에서 데이터를 조회하고
    1. 값이 있으면 반환
    2. 값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환한다.
  4. 이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치치 않아도 되므로 매우 빠르게 데이터를 조회할 수 있다.

2. 영속성 컨텍스트의 특징 - 쓰기 지연

모아서 처리

  1. 쓰기 지연(transactional write-behind)은 트랜잭션을 커밋하기 전까지는
    1. 데이터베이스에 실제로 질의문을 보내지 않고 쿼리를 모았다가
    2. 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행하는 것을 의미
  2. 예를 들어
    1. 데이터 추가 쿼리가 3개라면 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에
    2. 3개의 쿼리를 한꺼번에 쿼리를 전송한다.

3. 영속성 컨텍스트의 특징 - 변경 감지

현제 엔티티 값과, 1차캐시에 저장된 엔티티 값을 비교 -> 변경된 값을 자동 반영

  1. 트랜잭션을 커밋하면 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서
  2. 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영
    1. 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고,
    2. 데이터베이스 시스템의 부담을 줄일 수 있습니다.

4. 영속성 컨텍스트의 특징 - 지연 로딩

  1. 지연 로딩(lazy loading)은 쿼리로 요청한 데이터를 애플리케이션에 바로 로딩하는 것이 아니라
  2. 필요할 때 쿼리를 날려 데이터를 조회하는 것을 의미

<-> 즉시 로딩

반대로 조회할 때 쿼리를 보내 연관된 모든 데이터를 가져오는 즉시 로딩도 있다.

엔티티의 상태

엔티티는 4가지 상태를 가집니다.

  1. 영속성 컨텍스트가 관리하고 있지 않는 분리(detached) 상태
  2. 영속성 컨텍스트가 관리하는 관리(managed) 상태
  3. 영속성 컨텍스트와 전혀 관계가 없는 비영속 상태 (transient)
  4. 삭제된(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)
   }
 }
  1. 엔티티를 처음 만들면 엔티티는 비영속 상태가 됩니다.
  2. Persist() 메서드를 사용해 엔티티를 관리 상태로 만들 수 있으며
    • Member 객체는 영속성 켄텍스트에서 상태가 관리됩니다.
  3. 만약 엔티티를 영속성 컨텍스트에서 관리하고 싶지 않다면
    • Detach() 메서드를 사용해 분리 상태로 만들 수 있다.
  4. 또한 더 이상 객체가 필요 없다면 remove() 메서드를 사용해서
    • 엔티티를 영속성 켄텍스트와 데이터베이스에서 삭제할 수 있습니다.

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

개발자가 오직 로직에 신경쓰게 도와주는 스프링 데이터 JPA

  1. 스프링 데이터(spring data)는 비즈니스 로직에 더 집중할 수 있게
    • 데이터베이스 사용 기능을 클래스 레벨에서 추상화 하였다.
    • 스프링 데이터에서 제공하는 인터페이스를 통해서 스프링 데이터를 사용할 수 있다.
  2. 이 인터페이스에서는 CRUD를 포함한 여러 메서드가 포함되어 있으며
    • 알아서 쿼리를 만들어 준다.
    • 페이징 처리기능
    • 메서드 이름으로 자동으로 쿼리 빌딩하는 기능이 제공되는 등 많은 장점이 있다.
    • 각 데이터베이스의 특성에 맞춰 기능을 확장해 제공하는 기술도 제공

스프링 데이터 JPA란?

스프링 데이터의 공통적인 기능 + JPA의 기술

  1. 스프링 데이터 JPA는 스프링 데이터의 공통적인 기능에서 JPA의 유용한 기술이 추가된 기술
  2. 스프링 데이터 JPA에서는 스프링 데이터의 인터페이스인 PagingAndSortingRepository를 상속받아
    1. JpaRepository 인터페이스를 만들었으며
    2. JPA를 더 편리하게 사용하는 메서드를 제공한다.

Entity Manager 을 이용한 엔티티 상태 변경

 메서드 호출로 엔티티 상태 변경 예
 @PersistenceContext
 EntityManager em;
 ​
 public void join(){
   // 기존에 엔티티 상태를 바꾸는 방법(메서드 호출을 해서 상태 변경)
   Member member = new Member(1L, "홍길동");
   em.persist(member);
 }

Spring-data-JPA을 사용 예시

  1. 하지만 Spring-data-JPA를 사용하면 리포지터리 역할을 하는 인터페이스를 만들어
  2. 데이터베이스의 테이블 조회, 수정, 생성, 삭제 같은 작업을 간단히 할 수 있다.
  3. 다음과 같이
    1. JpaRepository 인터페이스를 우리가 만든 인터페이스에서 상속을 받고
    2. 제네릭에는 관리할 <엔티티 이름, 엔티티 기본키의 타입>을 입력하면
    3. 기본 CRUD를 위해 만든 메서드를 사용할 수 있다.
 // 기본 CRUD 메서드를 사용하기 위한 JpaRepository 상속예시
 public interface MemberRepository extends JpaRepository<Member, Long>{
   // Parameter
   //  관리한 엔티티 이름 : Member
   //  엔티티 기본키의 타입 : Long
 }

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

// JpaRepository에서 제공하는 메서드의 사용 방법을 보자.
@Service // Service <-  Repository -> DB
public class MemberService{
	@Autowired
  MemberRepository memberRepository; // Member(엔티티)관련 DB에 CRUD사용을 위한 레포지토리
  
  public void test(){
    // 1. 생성(Create)
    // save()메서드를 호출해 데이터 객체를 저장가능
    // 전달 인수로 엔티티 Member를 넘긴다.
    // 반환값으로 저장한 엔티티를 반환 받을 수 있다.
    memberRepository.save(new Member(1L, "A"));
    
    // 2. 조회(Read)
    // findById() 메서드에 id를 지정해 엔티티를 하나 조회 가능
    Optional<Member> member = memberRepository.findById(1L); // 단건 조회
    // findAll() 메서드는 전체 엔티티를 조회 가능
    List<Member> allMembers = memberRepository.findAll(); // 전체 조회
    
    // 3. 삭제(Delete)
    // deleteById() 메서드에 id를 지정하면 엔티티를 삭제 가능
    // 엔티티를 전달 인수로 넘겨 삭제할 수 있다
    memberRepository.deleteById(1L);
  }
}

Spring-boot-JPA을 활용한 계층별 코드 만들기

Member Entity의 변경

// Member.java

// 1. 엔티티로 지정
// @Entity 애너테이션은 Member 객체를 JPA가 관리하는 엔티티로 지정한다.
// Member 클래스와 실제 데이터 베이스의 테이블을 매핑
// @Entity의 속성중 name을 사용하면 name의 값을 가진 테이블 이름과 매핑
// 지정을 안하면 클래스 이름과 같은 데이터베이스의 테이블인 member 테이블과 매핑
// @Entity(name = "member_list")
@Entity
@Getter

// 2. 기본 생성자
// protected 기본 생성자이다.
// 엔티티는 반드시 기본 생성자가 있어야 하고, 접근 제어자는 public 또는 protected여야 한다.
// protected가 더 안전하다
@NoArgsConstructor(access = AccessLevel.PROTECTED) 
@AllArgsConstructor
public class Member{
  // 3. id 필들르 기본키로 지정
  // @Id는 Long 타입의 id 필드를 테이블의 기본키로 지정한다.
  @Id 
  
  // 4. 기본키 자동으로 1씩 증가.
  // @GeneratedValue는 기본키의 생성 방식을 결정한다.
  // 여기서는 자동으로 기본키가 증가하도록 지정했다.
  // 밑에 표로 정리했다.
  @GeneratedValue(strategy = GenerationType.IDENTITY) 
  @Column(name = "id", updatable = false)
  private Long id;
  
  // 5. name이라는 not null 컬럼과 매핑
  // @Column 애너테이션은 데이터베이스의 컴럼과 필드를 매핑해준다.
  // 대표적인 @Column 애너테이션의 속성 표 밑에 있다.
  @Column(name = "name", nullable = false)
  private String name;
}
  • @GeneratedValue 자동키 생성 설정 방식

설정 방식설명

AUTO 선택한 데이터베이스 방언에 따라 방식을 자동으로 선택 (기본값)
IDENTITY 기본 키 생성을 데이터베이스에 위임 (AUTO INCREMENT와 동일) - 증가
SEQUENCE 데이터베이스 시퀀스를 사용하여 기본 키를 할당하는 방법
TABLE 키 생성 테이블 사용
  • @Column( )

속성설명

name 필드와 매핑할 컬럼 이름입니다. 설정하지 않으면 필드 이름과 동일하게 지정됩니다.
nullable 컬럼의 null 허용 여부를 나타냅니다. 설정하지 않으면 true로 설정되어 컬럼이 null을 허용합니다.
unique 컬럼의 유일한 값 여부를 나타냅니다. 설정하지 않으면 false로 설정되어 컬럼은 중복 값을 가질 수 있습니다.
columnDefinition 컬럼의 세부 정보를 설정합니다. 컬럼에 대한 추가적인 설정이 필요한 경우 사용할 수 있습니다.

MemberRepository 파일

// 기본 CRUD 메서드를 사용하기 위한 JpaRepository 상속예시
public interface MemberRepository extends JpaRepository<Member, Long>{
  // Parameter
  //	관리한 엔티티 이름 : Member
  //	엔티티 기본키의 타입 : Long
}
  • 리포지토리는 엔티티에 있는 데이터들을 조회, 저장, 변경, 삭제를 할 때 사용하는 인터페이스
  • 스프링 데이터 JPA에서 제공하는 인터페이스인 JpaRepository 클래스를 상속 받아 간단하게 구현

  • JpaRepository 클래스를 상속 받을 때,
  • 엔티티 Member와 엔티티의 기본키 타입 Long을 인수로 넣어준다.

5장 학습 마무리

  • ORM (Object-Relational Mapping)
    • 관계형 데이터배이스와 프로그램 간의 통신 개념
    • 객체와 데이터베이스를 연결하는 프로그래밍 기법
  • JPA (Java Persistence API)
    • Persistence는 데이터를 생성, 읽기, 갱신, 삭제(CRUD)하는 작업을 지속적으로 유지하는 것을 의미합니다. 데이터를 영구적으로 보존하고 사용자가 필요할 때 언제든지 접근할 수 있도록 하는 것을 목표로 합니다.
    • 자바 어플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 기술 명세
    • 자바에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스
      • 엔티티는 영속성을 가진 객체를 의미
      • 엔티티 매니저는 엔티티를 관리하며 조회, 삭제, 수정, 생성하는 역활
      • 엔티티 매니저를 만드는 곳이 엔티티 매니저 팩토리
      • 엔티티 매니저는 엔티티를 영속성 컨텍스트에 저장한다는 특징이 있다.
      • 영속성 컨텍스트는 1차 캐시, 쓰기 지연, 변경 감지, 지연 로딩의 특징을 가지고 있다.
      • 엔티티의 상태는 분리, 관리, 비영속, 삭제 상태로 나뉩니다.
  • 하이버네이트는
    • JPA의 구현체
    • JPA의 구현체 중 대표적인 구현체로
      • 자바 언어를 위한 ORM (Object-Relationship-Mapping) 프레임워크입니다
  • 스프링 데이터 JPA는
    • JPA를 쓰기 편하게 만들어 놓은 모듈
    • Hibernate와 같은 구현체들을 좀 더 쉽게 사용할 수 있도록 추상화한 것.
    • JPA를 한 단계 추상화시킨 Repository 인터페이스를 제공.
    • 개발자가 Repository 인터페이스에 정해진 규칙대로 메소드를 입력하면, Spring이 알아서 해당 메소드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록.
  •  
  •