본문 바로가기
Backend boot camp/Session3

[Spring MVC] JPA 데이터 액세스 계층

by orioncsy 2022. 11. 7.

JPA 개요

JPA 개념

JPA(Java Persistence API)

  • Java에서 사용하는 ORM 기술의 표준 사양
  • Java의 인터페이스로 정의, 구현체 따로 존재

Hibernate ORM

  • JPA 구현체로 Hibernate ORM, EclipseLink, DataNucleus 존재
  • Hibernate ORM는 JPA 기능 이외 자체 사용 API 지원

Data Access layer에서 JPA

  • 데이터 액세스 계층의 상단에 위치
  • JPA를 거쳐 구현체인 Hibernate ORM에서 데이터 처리 작업 작동
  • Hibernate ORM은 내부적으로 JDBC API 사용하여 DB 접근

JPA 의미

  • persistence는 영속성을 의미

Persistence Context

  • ORM은 객체와 DB table을 매핑하여 엔티티 객체 안에 포한된 정보를 테이블에 저장하는 기술
  • JPA는 테이블과 매핑되는 엔티티 객체 정보를 Persistence Context라는 곳에 보관
  • 보관된 엔티티 정보는 DB 테이블에서 데이터 가공하는 데 사용
  • 1차 캐시와 쓰기 지연 SQL 저장소라는 영역으로 구분
  • 엔티티 정보를 저장하는 API를 사용하면 1차 캐시에 엔티티 정보 저장

JPA API로 Persistence Context 이해

  • build.gradle dependencies에 의존 라이브러리 추가
    • implementation 'org.springframework.boot:spring-boot-starter-data-jpa' 추가
  • Application.yml 설정
spring:
  h2:
    console:
      enabled: true
      path: /h2     
  datasource:
    url: jdbc:h2:mem:test
  jpa:
    hibernate:
      ddl-auto: create
    show-sql: true 
  • ddl-auto: create는 jpa에서 사용하는 엔티티 클래스 정의하고 엔티티와 매핑되는 테이블을 DB에 자동으로 생성
    • Spring data JDBC에서는 schema.sql 파일에서 직접 테이블 생성을 지정해줘야 했지만 JPA가 자동으로 DB에 테이블을 생성
  • show-sql:true는 SQL 쿼리를 로그로 출력
  • JpaBasicConfig
    • JpaBasicConfig를 클래스로 @Configuration 붙여서 선언
    • 필드 맴버로 EntityManager, EntityTransaction 선언
    • @Bean을 붙여서 testJpaBasicRunner 메서드 선언 EntityManaberFactory를 매개변수로 받고 반환형으로 CommandLineRunner로 설정
    • 매개로 받은 EntityManagerFactory 객체에 .createEntityManager()를 사용해서 EntityManager 필드에 할당
    • EntityManger 필드 변수에 .getTransaction()을 사용해서 EntityTransaction에 할당
    • 마지막에 CommandLineRunner 객체를 람다 표현으로 정의해서 반환
  • Persistence context에 entity 저장
    • entity 클래스에 @Entity를 붙여 선언
    • 해당 클래스의 기본키에 해당하는 필드 맴버에 @Id를 선언
    • @GeneratedValue를 선언하여 DB 테이블에서 키본키가 되는 식별자를 자동 생성
    • JpaBasicConfig에서 EntityManager 객체에 .persist([엔티티명])을 통해 persistence context에 저장
    • 엔티티가 저장되었는지 확인하기 위해 EntityManager 객체에 .find([엔티티명.class], [해당 id 식별자 값])로 조회 가능
  • 실행
    • .persist(); [INSERT]
      • 1차 캐시에 엔티티 객체가 저장되고 쓰기 지연 SQL 저장소에 INSERT 쿼리가 등록
      • 쓰기 지연 SQL 저장소의 쿼리를 실행하기 위해서는 EntityTransaction 사용
      • EntityTransaction 객체를 EntityManager 객체에서 .getTransaction()을 통해 가져온다.
      • EntityTransaction 객체에서 .begin() 메서드를 통해 Transacation 시작
      • EntityTransaction 객체에서 .commit() 메서드를 통해 persistence context에 저장되어 있는 엔티티 객체를 DB 테이블에 저장
      • .commit() 메서드를 사용하면 쓰기 지연 SQL 저장소에 실행된 쿼리가 제거된다.
      • EntityManager 객체에서 .fin()를 호출하면 1차 캐시에서 객체를 찾고 없으면 테이블에 SELECT 쿼리를 전송하여 조회한다.
    • setter [UPDATE]
      • persist()를 사용하여 엔티티 객체를 1차 캐시에 저장한다.
      • find를 통해 해당 엔티티 객체를 찾아서 setter 메서드를 사용하여 값을 변경한다.
      • setter 메서드를 사용하는 것만으로도 업데이트 로직은 완성
      • commit()을 사용하면 쓰기 지연 SQL 저장소에 등록된 UPDATE 쿼리 실행
    • remove [DELETE]
      • persist()를 사용하여 엔티티 객체를 1차 캐시에 저장
      • find를 통해 엔티티 객체를 찾아서 EntityManager 객체에서 remove()를 사용해서 제거
    • EntityManager에서 flush() API
      • EntityTransaction 객체에서 commit() 메서드가 실행되면 JPA 내부적으로 EntityManager 에서 .flush()가 호출되어 영속성 콘텍스트 변경 내용을 DB에 반영

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

    JPA 엔티티 매핑
    • 엔티티 객체에 @Entity를 사용해 엔티티 클래스와 테이블 매핑
    • @Entity(name=””) name 속성을 사용하여 entity 이름 설정
    • @Table(name=””) name 속성을 사용하여 table 이름을 변경(주로 클래스 이름과 테이블 이름 다를 때 사용)
      • 둘 다 설정을 안할 경우 기본적으로 클래스 이름을 사용
    • @Table은 옵션이고 @Entity, @Id는 필수
    • 파라미터가 없는 기본 생성자(@NoArgsConstructor) 필수
    기본 키 매핑
    • 기본적으로 @Id를 추가한 필드가 기본 키 칼럼으로 설정
    • 기본 키 생성 전략
      • IDENTITY
        • 기본키 생성을 DB에 위임하는 전략
        • MySQL의 AUTO_INCREMENT 기능을 통해 자동 증가 숫자를 기본키로 사용하는 방식 존재
      • SEQUENCE
        • DB에서 제공하는 시퀀스를 사용해 기본키 생성
      • TABLE
        • 별도의 키 생성 테이블 사용
    • 예시
      • 엔티티 객체에서 해당 필드에 @Id만 붙이면 기본키를 직접 할당해서 엔티티를 저장한다
      • 실제 저장할 때 em.persist(new Member(1L));
        • 1L이라는 기본키를 직접 할당한다.
      • IDENTITY 전략
        • 객체 엔티티에 해당 필드에 @GeneratedValue(strategy=GenerationType.IDENTITY)를 붙인다.
        • 데이터베이스에서 기본키를 대신 생성
      • SEQUENCE 전략
        • 객체 엔티티에 해당 필드에 @GeneratedValue(strategy=GenerationType.SEQUENCE)를 붙인다.
        • 데이터베이스 시퀀스 사용
        • 영속성 컨텍스트에 저장되기 전에 DB에 시퀀스에서 기본키에 해당하는 값 제공
      • @GeneratedValue(strategy = GenerationType.AUTO)
        • JPA가 자동으로 Dialect에 맞게 적절한 전략 선택
        • Dialect
          • 표준SQL이 아닌 특정 DB에 특화된 고유 기능
    필드(멤버 변수)와 칼럼 간의 매핑
    • @Column 애너테이션으로 필드와 칼럼을 매핑
    • @Column 애너테이션이 존재하지 않는다고 한다면 기본적으로 필드와 테이블 칼럼과 매핑되는 필드로 간주하고 attribute는 defualt로 적용
    • attribute
      • nullable
        • 컬럼에 null 허용할지 여부 지정
        • 디폴트는 true
      • updatable
        • 칼럼 데이터를 수정할 수 있는 지정
        • 디폴트 true
      • unique
        • 하나의 칼럼에 unique 제약 조건 설정
        • 디폴트 false
      • 원시 타입은 null값을 가질 수 없어 nullable은 false를 두는 것이 좋다
      • length
        • 문자열 길이 지정, 디폴트 255
      • java.util.Date, java.util.Calendar 타입으로 매핑하려면 @Temporal이 붙어야 하지만 LocalDateTime은 TIMESTAMP 타입과 자동 매핑
    • @Transient
      • 테이블 칼럼과 매핑하지 않는다고 JPA 인식
      • 임시 데이터를 메모리에서 사용하기 위한 용도
    • @Enumerated
      • enum 타입과 매핑할 때 사용하는 애너테이션
        • EnumType.ORDINAL은 순서를 나타내는 숫자를 테이블에 저장
        • EnumType.STRING은 enum 이름을 테이블에 저장
        • 순서가 바뀔 수 있으므로 STRING 권장
  • 엔티티와 테이블 간 매핑

JPA 엔티티 간의 연관 관계 매핑

연관 관계 매핑

  • 단방향 연관관계
    • 한쪽 클래스가 다른 쪽 클래스의 참조 정보를 단방향으로만 가지고 있는 경우
  • 양방향 연관관계
    • 서로 참조하고 있는 클래스 정보를 가지고 있어서 양방향으로 상대 정보를 알 수 있는 경우
  • JPA는 단방향, 양방향 모두 지원, Spring Data JDBC는 단방향 연관관계 지원

일대다 단방향 연관 관계

  • 일에 해당하는 클래스가 다에 해당하는 객체를 참조하는 관계
  • List 객체를 참조하여 사용
  • 주로 사용하지 않는다
    • DB에서는 다에 해당하는 테이블이 일에 해당하는 기본키를 외래 키로 가진다
    • 그러나 일대다 단방향 연관관계는 일에 해당하는 클래스가 참조하는 구조라 정상적으로 표현 불가

다대일 단방향 연관 관계

  • 다에 해당하는 클래스가 일에 해당하는 객체를 참조하는 관계
  • 외래 키를 가지는 방식과 유사하여 기본적인 매핑 관계로 사용
  • 구현
    • @ManyToOne 애너테이션과 @JoinColumn(name=”MEMBER_ID”)를 붙인 필드를 다에 해당하는 클래스에 작성
    • @JoinColumn(name=”MEMBER_ID”) name에는 외래 키에 해당하는 칼럼명

다대일 단방향 연관 관계에 일대다 단방향 연관관계 추가

  • 다대일 매핑에서 일대다 매핑을 추가하여 양방향 매핑 구현
  • 구현
    • 일에 해당하는 객체에서 @OneToMany(mappedBy=”member”)를 추가한 다에 해당하는 객체 리스트를 선언
    • mappedBy에는 다에 해당하는 객체에서 필드 멤버 중에 외래 키 역할을 하는 필드 이름을 적는다.

다대다 연관 관계 매핑

  • 다대다 중간에 하나의 테이블을 만들어 1대 다 관계, 다대 1 관계를 만든다
  • 다대일 매핑을 두 번 사용하여 매핑
  • 다대일 매핑을 통해 객체 그래프 탐색으로 원하는 객체를 조회할 수 없다면 양방향 매핑을 추가

일대일 연관 관계 매핑

  • 다대일 연관관계 매핑과 동일
    • @OneToOne 애너테이션으로 바꾸고 다른 것은 동일
  • 다대일에 일대다 매핑을 추가하는 방식도 동일
    • @OneToOne 애너테이션으로 바꾸고 다른 것은 동일

Spring Data JPA를 활용한 데이터 액세스 계층 구현

Spring Data JPA

JPA, Hibernate ORM, Spring Data JPA

  • JPA는 java에서 DB를 사용하기 위해 정해 놓은 표준 스펙
  • Hibernate ORM은 JPA의 구현체 중 하나(실제 사용하는 API)
  • Spring Data JPA는 JPA 구현체를 더 쉽게 사용할 수 있게 해주는 모듈

Spring JPA 적용 순서

  • 엔티티 클래스를 Spring Data JPA에 맞게 수정
  • repository 인터페이스 구현
  • 서비스 클래스 구현
  • 기타 수정 코드

엔티티 클래스를 Spring Data JPA에 맞게 수정

  • @Entity를 선언하고 @id를 지정하고 @Column을 설정한다

Repository 구현

  • JpaRepository <Member, Long>을 상속받는 인터페이스 선언
  • findByXXX 형태로 바디 구성
  • JPQL을 통한 객체 지향 쿼리 사용
    • JPQL이라는 객체 지향 쿼리를 통해 DB 테이블 조회 가능
    • 테이블 대상이 아니라 엔티티 클래스의 객체를 대상으로 조회하는 방법
    • “SELECT c FROM Coffee c WHERE c.coffeeId = :coffeeId"
    • COFFEE 테이블이 아니라 Coffee 엔티티를 참고하고, * 대신 c를 사용하는 등 SQL과 다르다
    • SELECT c는 생략 가능하다
  • 네이티브 SQL을 통한 조회
  • @Query(value = "SELECT * FROM COFFEE WHERE coffee_Id = :coffeeId", nativeQuery=true)
    • nativeQuery를 true로 설정하면 SQL문 사용 가능
  • Spring Data JDBC의 @Query : org.springframework.data.jdbc.repository.query.Query
  • Spring Data JPA의 @Query : org.springframework.data.jpa.repository.Query

'Backend boot camp > Session3' 카테고리의 다른 글

[Spring MVC] Testing  (0) 2022.11.11
[Spring MVC] 트랜잭션  (0) 2022.11.07
[Spring MVC] JDBC DB Access Layer  (0) 2022.10.29
[Spring MVC] Exception Handle  (0) 2022.10.29
[Spring MVC] Service layer  (0) 2022.10.29