49DAYS) [Spring MVC] JPA 기반 데이터 액세스 계층 - JPA (Java Persistence API), 엔티티 매핑

nacSeo (낙서)·2022년 12월 27일
0

◉ 학습목표

1. JPA가 무엇인지 알고, 동작 방식에 대해 이해할 수 있다.
2. JPA 엔티티에 대한 매핑을 할 수 있다.
  1. JPA (Java Persistence API)

⦿ 학습내용

☞ JPA (Java Persistence API)

✔︎ Java 진영에서 사용하는 ORM(Object-Relational Mapping) 기술의 표준 사양(또는 명세, Specification)

☞ Hibernate ORM

✔︎ JPA에서 정의해둔 인터페이스를 구현하는 구현체
✔︎ JPA에서 지원하는 기능 외에도 Hibernate 자체적으로 사용할 수 있는 API 지원

☞ 영속성 컨텍스트 (Persistence Context)

✔︎ JPA에서는 테이블과 매핑되는 엔티티 객체 정보영속성 컨텍스트에 보관해서 애플리케이션 내에서 오래 지속되도록 함
✔︎ 그림으로 표현한 영속성 컨텍스트

☞ JPA API로 영속성 컨텍스트 이해하기

✔︎ build.gradleimplementation 'org.springframework.boot:spring-boot-starter-data-jpa' 추가
✔︎ JPA 설정 (application.yml)

jpa:
    hibernate:
      ddl-auto: create  # (1) 스키마 자동 생성
      show-sql: true    # (2) SQL 쿼리 출력
  • (1) : JPA가 자동으로 데이터베이스에 테이블을 생성
  • (2) : JPA API를 통해 실행되는 SQL 쿼리를 로그로 출력

✔︎ Configuration 클래스 생성

  • 특정 클래스에 @Configuration 애너테이션 추가, 메서드에 @Bean 애너테이션 추가
    • Spring Bean의 검색 대상인 Configuration 클래스에서 해당 메서드에서 리턴하는 객체를 Spring Bean으로 추가
  • CommandLineRunner 객체람다 표현식으로 정의
    • 람다 표현식에 정의한 코드 실행

✔︎ 엔티티 클래스 생성

  • 엔티티 클래스에 @Entity 애너테이션 추가, 고유값을 나타내는 엔티티 객체에 @Id 추가
    • 해당 애너테이션들이 있으면 JPA에서 해당 클래스를 엔티티 클래스로 인식

✔︎ 영속성 컨텍스트에 엔티티 저장

  • EntityManager 클래스에 의해 관리
  • EntityManager 클래스의 객체는 EntityManagerFactory 객체를 Spring으로부터 DI 받을 수 ⭕️
  • EntityManagerFactorycreateEntityManager() 메서드를 이용해 EntityManager 클래스 객체 얻을 수 ⭕️
  • EntityManager 클래스의 객체를 통해 JPA의 API 메서드 사용 가능

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

  • EntityManager를 통해 Transaction 객체 얻음
  • JPA에서는 Transaction 객체를 기준으로 데이터베이스의 테이블에 데이터 저장

☞ 영속성 컨텍스트 관련 JPA API

✔︎ tx.begin() : Transaction 실행하는 메서드
✔︎ em.persist()

  • 엔티티 객체를 영속성 컨텍스트에 저장
  • 1차 캐시해당 객체 저장
  • 쓰기 지연 SQL 저장소INSERT 쿼리 형태로 등록

✔︎ tx.commit()

  • 호출되는 시점에 영속성 컨텍스트에 저장되어 있는 객체를 데이터베이스의 테이블에 저장
  • 1차 캐시에는 해당 객체만 남고, 쓰기 지연 SQL 저장소에 INSERT 쿼리는 사라짐
  • 내부적으로 em.flush() 호출
    em.flush() : 영속성 컨텍스트의 변경 사항을 테이블에 반영

✔︎ em.find() : 영속성 컨텍스트에 잘 저장되었는지 조회

  • 첫 번째 파라미터 : 조회할 엔티티 클래스의 타입
  • 두 번째 파라미터 : 조회할 엔티티 클래스의 식별자 값
  • 예시
em.find(Member.class , 1L)	// Member 클래스 타입, 식별자 값은 Long타입 1

find()를 통해 해당 식별자 값의 객체가 존재하지 않을 경우, 테이블에 직접 SELECT 쿼리 전송

✔︎ 엔티티 객체의 setter 메서드를 사용하면 영속성 컨텍스트에 저장된 엔티티 객체의 정보를 업데이트
✔︎ em.remove() : 엔티티 객체를 영속성 컨텍스트에서 제거

  1. 엔티티 매핑

⦿ 학습내용

☞ 엔티티 매핑 작업 종류

✔︎ 객체(엔티티)와 테이블 간의 매핑
✔︎ 기본키 매핑
✔︎ 필드(멤버 변수)와 컬럼 간의 매핑
✔︎ 엔티티 간의 연관 관계 매핑 (※ 내일 학습 예정)

☞ 엔티티와 테이블 간의 매핑

✔︎ @Entity

  • 해당 애너테이션을 클래스 레벨에 추가하면 JPA의 관리대상 엔티티가 됨
  • 애트리뷰트
    • name : 엔티티 이름 설정, 미설정시 기본값으로 클래스 이름을 엔티티 이름으로 사용

✔︎ @Table

  • 엔티티와 매핑할 테이블 지정
  • 애트리뷰트
    • name : 테이블 이름 설정, 미설정시 기본값으로 클래스 이름을 테이블 이름으로 사용

✔︎ @Entity 애너테이션과 @Id 애너테이션은 필수❗️로 추가

☞ 기본키 매핑

✔︎ 기본키 직접 할당 : 애플리케이션 코드 상에서 기본키 직접 할당

  • @Id 애너테이션만 추가해 기본키를 코드 상 직접 할당
    ✔︎ 기본키 자동 생성
  • IDENTITY 전략 : 기본키 생성을 데이터베이스에 위임
    • @GenerateValue(strategy = GenerationType.IDENTITY)로 지정
  • SEQUENCE 전략 : 데이터베이스에서 제공하는 시퀀스를 이용하여 기본키 생성
    • @GenerateValue(strategy = GenerationType.SEQUENCE)로 지정
  • TABLE 전략 : 별도의 키 생성 테이블 사용
    • 키 생성 전용 TABLE을 별도로 만들어야 하고 키 조회 및 업데이트 쿼리를 추가로 전송해야 함
    • 따라서 성능면에서 좋은 선택 ❌
  • AUTO 전략 : JPA가 데이터베이스의 Dialect에 따라 적절한 전략을 자동으로 선택
    • @GenerateValue(strategy = GenerationType.AUTO)로 지정
      Dialect : 표준 SQL 등이 아닌 특정 데이터베이스에 특화된 고유한 기능을 의미

☞ 필드(멤버 변수)와 컬럼 간의 매핑

✔︎ @Column

  • 필드컬럼을 매핑해주는 애너테이션
  • 애트리뷰트
    • nullable
      • 컬럼에 null값을 허용할지 여부 지정
      • 디폴트 값 : true
    • updateable
      • 컬럼 데이터를 수정할 수 있는지 여부 지정
      • 디폴트 값 : true
    • unique
      • 하나의 컬럼에 unique 제약 조건 설정
      • 디폴트 값 : false
  • 생략 시 주의 사항 🚨
    • int나 long같은 원시 타입일 경우, 기본적으로 nullable = false
    • int price not null일 경우, @Column(nullable=false)라고 명시하거나 @Column 애너테이션 사용 ❌

✔︎ java.util.Date, java.util.Calendar 타입으로 매핑하기 위해서는 @Temporal 애너테이션을 추가해야 하지만
LocalDate, LocalDateTime 타입일 경우, @Temporal 애너테이션 생략 가능
✔︎ @Transient 애너테이션을 필드에 추가하면 JPA가 테이블 컬럼과 매핑하지 않겠다는 의미로 인식
✔︎ @Enumerated

  • enum 타입과 매핑할 때 사용하는 애너테이션
  • 두 가지 타입
    • EnumType.ORDINAL : enum의 순서를 나타내는 숫자를 테이블에 저장
    • EnumType.STRING : enum의 이름을 테이블에 저장

🚨 주의사항 : 테이블에 이미 저장되어 있는 enum 순서 번호와 enum에 저장되어 있는 순서가 일치하지 않는 문제가 발생하지 않도록 EnumType.STRING 사용 권장 ❗️

◉ 느낀 점

☞ JPA를 공부하면서 어려웠지만 미리 JDBC에 대해 먼저 공부해둔 것이 그래도 구조를 이해하는 데에 어느 정도 도움이 되었다.

처음 만난 내용이면서 중요했던 내용이 영속성 컨텍스트라는 내용인데 해당 내용들을 공부하다보니 Git의 add와 commit이 생각났다. persist를 사용해서 컨텍스트에 add해주고, commit을 이용해 영속성 컨텍스트의 쓰기 지연 SQL 저장소에 INSERT 쿼리가 사라지면서(git의 add한 정보를 올리는 느낌) 데이터베이스의 테이블에 저장시키는 구조가 참 닮았다고 생각했다.

여러 가지 엔티티 매핑 작업 중 가장 어려우면서 중요한 엔티티 간의 연관 관계 매핑을 제외하고 (내일 공부할 예정!) 객체와 테이블 간의 매핑, 기본키 매핑, 필드(멤버 변수)와 컬럼 간의 매핑에 대해 알아보며 여러 애너테이션들과 JPA API들을 직접 입력해보며 익혔다. 람다 표현식에 정의할 코드를 학습 내용 이외에 내가 직접 궁금한 점들을 입력해보고 매핑해보며 좀 더 이해하려 노력했다. 앞으로도 이러한 방식으로 공부한다면 좀 더 잘 이해할 수 있을 거란 생각이 들었다.

내일 역시 연관 관계 매핑에 대해 학습하게 되는데, 이해하려 최선을 다해보겠다.

◉ 내일의 키워드

・ 연관 관계 매핑
profile
백엔드 개발자 김창하입니다 🙇‍♂️

0개의 댓글