[22-07-(05~7)]Spring Data JPA

이상수·2022년 7월 23일
0

TIL_Spring MVC

목록 보기
7/11
post-thumbnail
  1. 시작하게 된 계기 및 다짐 😮
  • 이번 코드스테이츠의 백엔드 엔지니어링 개발자 부트캠프에 참여하게 되면서 현직개발자 분들의 빠른 성장을 위한 조언 중 자신만의 블로그를 이용하여 배운 것 들을 정리하는게 많은 도움이 된다 하여 시작하게 되었다.

    • 그 날 배웠던 것을 길지 않아도 좋으니 정리하며 복습하는 습관 기르기
    • 주말에 다음주에 배울 내용들을 예습
    • 코딩 문제와 java코드들은 꾸준히 학습
    • 자료구조를 이용한 알고리즘 문제 해결 학습
  1. 학습 목표 😮
목표결과
- JPA 엔티티에 대한 매핑을 할 수 있다.O
JPA 기반의 엔티티 간 연관 관계를 매핑O
JPA가 무엇인지 이해 및 동작 원리O
Spring Data JDBC를 이용해서 데이터의 저장, 수정, 조회, 삭제 작업O
Spring Data JDBC 기반의 엔티티 연관 관계를 매핑O
  1. 정리 😮

JPA(Java Persistence API)란?


0. @애너테이션

  1. @Entity

    • @Id애너테이션과 같이 추가해주면 JPA에서 해당 클래스를 엔티티 클래스로 인식'
  2. @GeneratedValue

    • 해당 엔티티의 식별자를 생성해주는 전략을 지정할 때 사용
    • 테이블에서 기본키가 되는 식별자를 자동으로 설정해 준다.
    • 추후, 자세한 내용

1. JPA란?

  • Java에서 사용하는 ORM 기술의 표준 사양(인터페이스)
  • 인터페이스로 구현되어있고, JPA라는 표준 사양을 구현한 구현체는 따로 있다.
    1). 구현체
    - Hibernate ORM, EclipseLink, DataNucleus중, "Hibernate ORM"이 학습할 구현체
    - JPA는 Jakarta Persistence라고도 불림

2. 데이터 엑세스 계층에서의 JPA위치

  • 사진
  • 실제 데이터의 작업,조회등 작업은 JPA를 거쳐 JPA의 구현체인 Hibernate ORM을 통해 이루어지고,
    이 Hibernate ORM이 내부적으로 JDBC API를 이용하여 DB에 접근하게 된다.

3. 영속성 컨텍스트 _ JPA에서의 P의 의미

  • 무언가를 금방 사라지지 않고 오래 지속되게 한다 라는 목적

####. 1. 영속성 컨텍스트(Persistence Context) [사진]
- ORM은 객체와 테이블의 매핑을 통해 엔티티 클래스 객체 안에 포함된 정보를 테이블에 저장하는 기술
- 이때, 매핑된 엔티티 객체 정보를 이 영속성 컨텍스트 안에 보관하여 오래 머물도록 해준다.
- '1차 캐시'와 '쓰기 지연 SQL저장소'라는 영역이 있다.
- 저장시, 먼저 1차 캐시에 엔티티 정보가 저장됨

[JPA API 사용 설정 기반 코드
dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // (1)
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

   (1) 기본적인 Spring Data JPA 기술 사용
}

[JPA 설정(application.yml)]
spring:
  h2:
    console:
      enabled: true
      path: /h2     
  datasource:
    url: jdbc:h2:mem:test
  jpa:
    hibernate:
      ddl-auto: create  # (1) 스키마 자동 생성
    show-sql: true      # (2) SQL 쿼리 출력

(1) 엔티티 클래스를 정의하고 실행 시, 매핑되는 테이블을 DB에 자동으로 생성해준다.
     - 기존 JDBC에서는 스키마를 직접 지정했지만 JPA가 자동으로 DB에 테이블 생성
(2) JPA API를 통해 실행되는 SQL 쿼리를 로그로 출력해준다.

4. 영속성 컨텍스트 사용 Code

  1. CommandLineRunner

    • 이 CommandLineRunner 객체를 람다 표현식으로 정의해주면, 애플리케이션이 부트스트랩 과정이
      완료 된 후에 이 람다 표현식에 정의한 코드를 실행해준다.
    • 부트스트랩 : 일반적으로 한 번 시작되면 알아서 진행되는 일련의 과정

  1. 영속성 컨텍스트에 엔티티 저장

    1). 예제 Code 분석
    (1) EntityManagerFactory의 createEntityManager()를 통해서 DI를 받아 객체를 얻을 수 있음
    - 이 EntitiyManger 클래스 객체를 통해 JPA API 메서드를 이용할 수 있음
    (3) em.persist(member)코드를 통해 member엔티티를 영속성 콘텍스트에 등록한다.
    - EntityManger클래스 객체를 통해 Member등의 엔티티를 영속성 컨텍스트에 저장할 수 있다.
    - em.persist(member)코드를 실행하고나서, '1차캐시'엔 'Member1' 그리고 '쓰기 지연 SQL저장소'에는 'INSERT...(member1)' 의 쿼리문이 등록되지만, 실제 테이블에 회원 정보를 등록하지는 않는다.
    (4) em.find(조회 할 엔티티 클래스 타입, 조회 할 엔티티 클래스 식별자 값)

[예제 Code]
@Cofiguration
public class {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) { // (1)
        this.em = emFactory.createEntityManager();  // (2)

        return args -> {
            Member member = new Member("hgd@gmail.com");
        
            // (3)
            em.persist(member);
            // (4)					
            Member resultMember = em.find(Member.class, 1L);
            System.out.println("Id: " + resultMember.getMemberId() + ", email: " + 
                    resultMember.getEmail());
        };
    }
}

  1. 영속성 컨텍스트와 테이블에 엔티티 저장

    1). 예제 Code 분석
    (1) EntityManager 객체로부터 Transaction객체를 얻어 이 객체로 DB테이블에 데이터를 저장
    (2) Transcation을 시작하기 위해 tx.begin()을 호출필요
    (4) tx.commit()을 호출하는 시점에서 영속성 컨텍스트에 저장되어 있는 객체를 DB 테이블에 저장한다.
    - 이 시점에서 '쓰기 지연 SQL 저장소'의 SQL문이 사용되어 사라짐
    (6) member2의 경우, 영속성 컨텍스트에 없으면 직접 SELECT 쿼리문을 요청해 테이블에 조회를 한번더 한다.
    [Hibernate ORM 구현체가]
    ★ 즉, 1차로 영속성 컨텍스트에 조회하고 없을시 SELECT 쿼리문을 통해 테이블에 직접 조회한다.

[예제 Code]
@Cofiguration
public class {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
        this.em = emFactory.createEntityManager();
       
        // (1)
        this.tx = em.getTransaction();
        return args -> {					
            tx.begin();  // (2)
            Member member = new Member("hgd@gmail.com");			
            em.persist(member); // (3)	
            tx.commit(); // (4)

            // (5)					
            Member resultMember1 = em.find(Member.class, 1L);
            System.out.println("Id: " + resultMember1.getMemberId() + ", email: " +
                    resultMember1.getEmail());

            // (6)					
            Member resultMember2 = em.find(Member.class, 2L);

            // (7)				
            System.out.println(resultMember2 == null);
        };
    }
}

  1. 쓰기 지연을 통한 영속성 컨텍스트와 테이블에 엔티티 일괄저장

    • 영속성 컨텍스트에 em.persist(member1~n)까지 저장 한 후, 원하는 시기에 tx.commit()을 통해 컨텍스트에 저장된 데이터들을 일괄적으로 DB 테이블에 저장 할 수 있다.
  1. 영속성 컨텍스트와 테이블 엔티티 업데이트 / 삭제

1). 테이블 엔티티 업데이트 과정

(1). Transaction을 시작한다는 tx.begin() 이후, update하고자하는 값을 em.find(클래스,식별자 값)을 통해서 찾음
(2). 해당 값을 setter메서드를 통해 값을 변경 시킨후, tx.commit()을 하면 완료가 됨 ( 따로 업데이트 해주는 메서드는 없음)
(3). 영속성 컨텍스트에 저장되는 엔티티의 경우, 저장 되는 시점의 상태를 가지고 스냅샷을 생성하는데, 이 때 setter메서드를 통해 값이 변경되면 tm.commit()시기에 이 스냅샷과 변경된 엔티티를 비교하여 '쓰기 지연 SQL 저장소'에 UPDATE 쿼리문을 생성하고 이를 실행한다.

2). 테이블 엔티티 삭제 과정
1). tx.begin()이후, 삭제하고 하는 엔티티를 em.find()메서드를 통해 찾은 후, em.remove(엔티티)를 통해 해당 엔티티를 삭제한다.
2). 이후, tx.commit()을 통해 테이블에 이 삭제된 데이터 정보를 등록
3). 위와 마찬가지로, em.remove()로 1차 캐시의 엔티티가 제거되고 이 후, 쓰기 지연 SQL저장소에 등록된 DELETE 쿼리가 실행된다.

★ tx.commit() 메서드가 호출되면, JPA 내부적으로 em.flush() 메서드가 호출되어 영속성 컨텍스트의 변경 내용을 DB에 반영한다.


Extra

  1. JPA의 의미
  2. JPA 생명 주기
  3. Hibernate ORM
  4. JPA 엔티티를 테이블로 자동 생성 해주는 ddl-auto 기능



JPA 엔티티 매핑과 연관관계 매핑


0. @애너테이션

  1. @Entity
    • (name="USER") 애트리뷰트를 통해, 엔티티의 이름을 설정 할 수 있음
    • 비 설정시, 클래스 이름을 엔티티 이름으로 사용한다.
  2. @Table
    • (name="USER") 애트리뷰트를 통해, 테이블 이름을 설정 할 수 있음
  3. @GeneratedValue
    • 기본키를 매핑해주는 방식을 지정해주는 애너테이션
    • (strategy= GenerationType.IDENTITY/SEQUENCE)
  4. @Profile

1. 엔티티 테이블 간의 매핑

  1. 클래스 레벨에 @Entity 애너테이션을 붙여 JPA 관리 대상 엔티티로 지정
    • 엔티티 클래스와 테이블을 매핑
  2. @Table 애너테이션의 경우 옵션이지만, @Entity와 @Id 애너테이션은 필수이다.
    • @Entity와 @Id(식별자)는 함께 사용해야 한다.
  3. 기본 생성자의 경우, 습관적으로 추가해 주는것이 좋다.

2. 기본키 매핑

  • JPA에서는 기본적으로, @Id가 붙은 필드를 테이블의 기본키 컬럼이 되는데, 이 기본키를 어떤 방식으로 생성해 줄지 설정할 수 있다.
  1. 기본키 생성 전략
    1). 기본키 직접 할당
    - 애플리케이션 코드 상에서 기본키를 직접 할당
    - @Id 애너테이션을 사용하여 기본키를 직접 할당
    - 기본키 필드값을 받는 생성자를 생성

    2). 기본키 자동 생성
    0). Repository.save() == persist 시점

    1). IDENTITY
    - 기본키 생성을 DB에 위임, MySQL 방식의 AUTO_INCREMENT 기능을 통해 자동 숫자 증가를 기본키
    - @Id + @GeneratedValue 애너테이션과 애트리뷰트의 값(strategy= GenerationType.IDENTITY) 지정
    - 생성자가 없는 기본 생성자로 생성하면, DB가 자동생성
    ★다른 기본키 생성과 달리, 영속성 컨텍스트에 등록을 하기 위해 기본키가 필요하여, em.persist()메서드 호출시에 flush => Insert문이 실행이 되어 테이블에 데이터를 저장 후 기본키가 생성된 후 영속성 컨텍스트에 저장
    즉, Transcation을 지원하는 쓰기 지연 방식이 동작하지 않는다.
    ★ em.find()를 찾기전에, tx.commit이 필요

    2). SEQUENCE
    - DB에서 제공하는 시퀸스를 사용해 기본키 생성 전략
    - @Id + @GeneratedValue 애너테이션과 애트리뷰트의 값(strategy= GenerationType.SEQUENCE) 지정
    ★ em.persist() 메서드 호출시, DB Sequence를 조회 후 조회한 식별자를 entity에 할당하여 영속성 컨텍스트에 저장한다. 이 후, commit 하여 flush가 발생하면 entity를 DB에 저장
    ★ em.find() 이후 commit해도 돌아감

    3). AUTO
    - GnerationType.AUTO를 지정하면, JPA가 DB의 Dialect에 따라서 적절한 전략을 자동으로 선택

    0). TABLE
    - 별도의 키 생성 테이블을 사용하는 전략
    - 성능면에서 좋은 선택은 아니라, 사용하지 않음 보통


3. 필드와 컬럼간의 매핑

  1. @애너테이션
    1). @Column(nullable=false[true], updatable = false[true], unique = true[false])
    - 애트리뷰트 - [length(최대 길이),name(column이름), )
    - 필드와 컬럼을 매핑해주는 애너테이션으로, 붙이지 않아도 필드는 모두 컬럼으로 매핑하고 디폴트값으로 적용됨

    • []안에 있는 값이 기본 default값이다
    • 각 null포함여부, 컬럼 수정여부, 유니크 제약조건을 의미한다.
    • 원시타입의 경우 null이 값으로 지정될 수 없기에, 최소한 nullable은 false로 설정하는 것이 에러를 방지

    2). @Transient
    - 해당 필드를 테이블의 컬럼과 매핑하지 않겠다는 의미이다.
    - 따라서, DB에 저장도 하지않고, 조회시에도 매핑하지 않는다
    - 임시 데이터를 메모리에 저장하기 위한 용도

    3). @@Enumerated
    - enum 타입과 매핑할 때 사용하는 애너테이션으로,
    1). EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장
    2). EnumType.STRING : enum의 이름을 테이블에 저장

  1. 엔티티 클래스에서 발생한 예외처리

    • API 계층의 DTO클래스의 마찬가지로 일종의 유효성 검증이라 볼 수 있다.
    • 엔티티 클래스에서 발생한 예외는 API 계층까지 전파되므로, GlobalExceptionAdvice에서 캐치 한 후, 처리할 수 있다.
  1. 엔티티 테이블 매핑 권장 방법
      1. 클래스 이름 중복등의 특별한 이유가 없을시, @Id와 @Entity만 추가
      1. 기본키 전략은 DB에서 지원해주는 IDENTITY, SEQUECNE사용을 권장 (AUTO_INCREMENT)
      1. @Column 정보를 명시하는 것이 좋다
      1. 엔티티 필드가 원시타입일 경우, @Column의 nullable을 false로 설정
      1. @Enumerated애너테이션 사용시, EnumType.String 권장



엔티티 간의 연관관계 매핑


0. @애너테이션

  1. @ManyToOne

    • JPA에서 'N대1' 관계를 명시할 때 사용한다.
  2. @JoinColumn

    • 'N대 1'매핑관계에서 N쪽에서 사용
    • '외래키'에 해당하는 컬럼명을 알려줄 때 사용한다.
    • 'name' 애트리뷰트는 기본키 컬럼명과 동일하게 적어준다.
      ★ 참조하는 쪽에서 사용
  3. @OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST)

    • '1대N'의 매핑관계에서, 1쪽에서 사용하는 애너테이션으로, N쪽의 객체를 얻기위함
    • N쪽의 '외래키'를 받는 필드 변수를 mappledBy = '변수' 안에 넣어주는 방법으로 연관관계를 만든다.
    • 외래키 역할은 N쪽에 존재
    • .PERSIST : 부모엔티티를 영속성 등록시, 해당 자식 엔티티도 같이 영속성으로 등록해 주는것
      • 부모엔티티 등록시, 자식엔티티가 DB에 등록되 있지 않아 발생하는 오류 제거
      • 주로, 1:1 매핑이나 자식-부모간의 생명라이프가 같을 경우 사용한다.

    ★ 외래키의 소유자측이 보통 사용


1. 연간 관계 매핑 방식

  1. 단방향 연관 관계

    • 두 클래스 에서, 한쪽에는 다른 클래스의 객체를 참조할 수 있지만, 다른 클래스에서 한쪽 클래스의 객체를 참조할 수 없는 관계
    • 필드변수에 해당 클래스에 대한 참조 변수가 없음
  2. 양방향 연관 관계

    • 두 클래스에서 각 클래스에 대한 객체 참조 정보를 가지고있어 서로 참조할 수 있는 관계
      ★ JPA에서는 양방향/단방향 연관관계를 모두 지원하지만, Data JDBC에서는 단반향 연관 관계만 지원한다.
  3. 일대다 단방향 연관 관계

    • 다대일 단방향 매핑을 먼저 한 후, 필요한 경우 일대다 단방향 매핑을 추가하는 경우가 일반적이다.
  4. 다대일 연관 관계

    • '다' 쪽에서만 '일'쪽에 대한 객체참조를 즉, 외래키를 가지고있고 가장 많이 사용되는 매핑 방식이다.
  5. 다대다 연관 관계

    • 혼자 학습 권장

**2. 엔티티 간의 연관 관계 매핑 권장 방법

  1. 일대다 매핑은 사용하지 않음
  2. 먼저, 다대일 단방향 매핑부터 적용한다.
  3. 다대일 단방향 매핑을 통해 객체 그래프 탐색으로 조회할 수 없는 정보가 있을 경우, 그 떄 비로소 양방향 매핑을 적용한다.

Extra

  1. 엔티티 클래스 연관관계
  1. JPA에서 FETCH가 무엇이고, 방식은 무엇인지 참고
  1. JPA에서 CASCADE가 무엇인지 참고



Spring Data JPA를 통한 데이터 엑세스 계층 구현


1. Spring Data JPA란

  1. JPA vs Hibernate ORM vs Spring Data JPA [# 사진]
    1). JPA란,
    • Java 애플리케이션에서 관계형 데이터베이스를 사용하기 위해 정해 놓은 표준 스펙(사양 또는 명세)이다.
    • 즉, 기술이나 구현체가 아닌 기술 명세를 의미한다.
      [Ex. EntityManagerFactory , EntityManager , EntityTransaction]
    2). Hibernate ORM
    • JPA라는 표준 스펙을 구현한 구현체로써, 실질적으로 사용하는 API
    • JPA와 Hibernate는 마치 자바의 interface와 해당 interface를 구현한 class와 같은 관계
      [Ex. SessionFactory , Session , Transaction 으로 상속받고 각각 Impl로 구현]
    3). Spring Data JPA
    • JPA 스펙을 구현한 구현체 API(일반적으로, Hibernate ORM)을 조금 더 편하게 사용할 수 있게 해주는 모듈
  1. 리포지토리 구현
  • 각 클래스 Repository에서 JpaRepository를 상속하여 JPA에 특화된 더많은 기능들을 사용

    1). JPQL을 통한 객체 지향 쿼리

    • JPQL은 DB테이블을 대상을 조회하는 것이 아니라, 엔티티 클래스의 객체를 대상으로 조회를 하는 방법
    • JPA가 내부적으로 JPQL을 분석하여 적절한 SQL을 만든 후에 DB를 조회하고, 조회한 결과를 엔티티 객체로 매핑한 뒤 반환
      [Ex. @Query(value = "SELECT c FROM Coffee c WHERE c.coffeeId = :coffeeId")]

    2). 네이티브 SQL을 통한 조회

    • Spring Data JDBC에서도 네이티브 SQL쿼리를 이용한 작성 가능
    • nativeQuery 애트리뷰트의 값을 'true'로 설정시 value 애트리뷰트에 작성한 SQL 쿼리가 적용

    3). JPA를 통한 장점

    • 특정 기술에 강하게 결합되지 않도록, Spring이 추구하는 PSA(일관된 서비스 추상화)를 통해,
      개발자는 일관된 코드 유지 및 변경시 최소한으로 변경

Extra

  1. JPQL 정보
  1. Spring Data JPA
  1. 7월7일 강의 뒷부분 open-in-view 확인

  2. Hibernate 옵션

1). ddl-auto:

  • none : 아무 동작 하지 않습니다.
  • create-only : 테이블이 없을 경우 create합니다.
  • drop : 테이블을 drop 합니다.
  • create : 기존에 테이블이 존재할 경우 테이블 drop후 새로 create합니다.
  • create-drop : 앱 실행시 테이블 create하고 앱 종료시 테이블을 drop합니다.
  • validate : 엔티티 설정과 기존 테이블 설정이 다를 경우 에러 발생합니다.
  • update : 테이블, 컬럼정보가 달라졌을 경우 추가됩니다.



  1. 피드백 😮
  • Spring Data JPA란, JPA라는 ORM기반 기술의 표준 사양을 구현한 구현체인 Hibernate ORM을 좀더 쉽게 사용할 수 있게 해주는 모듈 이다.

  • 내부적으로 Spring Data JDBC를 사용하고, Jpa Repository를 상속하여 리포지토리를 인터페이스로 생성

  1. 앞으로 해야 될 것 😮
  • 매일 꾸준히 할 것
    • 꾸준히 velog 작성
    • Java 언어 및 Algorithm 공부(Coding-Test)
    • 틈틈히 운동 하기

  • 내일 해야 할 것
    • Spring 트랜잭션 학습
profile
Will be great Backend-developer

0개의 댓글