CodeStates/Section 03

[Spring] JPA

NYinJP 2023. 2. 25. 02:14

학습 목표

  • JPA가 무엇인지 이해할 수 있다.
  • JPA의 동작방식을 이해할 수 있다.
  • JPA API의 기본 사용방법을 이해할 수 있다.

개요

JPA란?

Java 진영에서 사용하는 ORM(Object - Relational Mapping) 기술의 표준 사양입니다.

표준 사양이란 의미는 인터페이스로 사양이 정의되어 있기 때문에 JPA라는 표준 사양(인터페이스)을 구현한 구현체는 따로 있다는 뜻입니다. 그리고 그 구현체에 대해 공부하는 것이 JPA(Java Persistence API) 학습입니다!

 

Hibernate ORM 구현체를 학습합니다.

 

데이터 액세스 계층에서의 JPA

JPA는 데이터 액세스 계층의 상단에 위치합니다.

JPA의 구현체인 Hibernate ORM을 통해서 데이터 저장, 조회가 이루어진다. 그리고 Hibernate ORM은 내부적으로 JDBC API를 이용해 DB에 접근한다.

 

영속성 컨텍스트

JPA(Java Persistence API) 대체 무엇을? Persistence? 영속적으로 오래 지속되게 하는가??

ORM은 객체와 데이터베이스 테이블의 매핑을 통해 엔티티 클래스 객체 안에 포함된 정보를 테이블에 저장하는 기술이다.

테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트라는 곳에 보관해서 애플리케이션 내에서 오래 지속되도록 한다.

Persistence Context

#설정추가

build.gradle에 dependencies에 설정을 추가해 줘야 Spring Data JPA 기술을 포함해서 JPA API를 사용할 수 있습니다.

1. build.gradle dependencies 설정
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

2. 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)을 통해 JPA가 자동으로 데이터베이스에 테이블을 생성해 준다.

(2)를 통해 SQL 쿼리를 로그로 출력해 준다.

 

 

 


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

Member 클래스

import lombok.Getter;

import javax.persistence.*;

@Getter
@Setter
@NoArgsConstructor
@Entity  // (1) 기본 설정 
public class Member {
    @Id  // (2) @Entity와 짝궁처럼
    @GeneratedValue  // (3)
    private Long memberId;

    private String email;

    public Member(String email) {
        this.email = email;
    }
}

@Entity, @Id 애너테이션은 JPA에서 해당 클래스를 엔티티로 인식합니다.

@GeneratedValue 애너테이션은 식별자를 생성해 주는 전략을 지정할 때 사용합니다.

 

어떻게 저장하는가?

EntityManagerFactory 객체를 Spring으로부터 DI 받아 createEntityManager() 메서드를 통해 객체를 생성합니다.

생성한 객체를 통해서 JPA API 메서드를 이용할 수 있습니다!

. persist

. find

등과 같은 메서드

em.persist(member)만 사용해서는 영속성 콘텍스트에 member객체를 저장하지만 실제 테이블에 정보를 저장하지 않는다.

 

tx.begin() - Transaction 실행 위해 먼저 호출

em.persist() - 영속성 컨텍스트에 저장(1차 캐시 객체 저장과 쓰기 지연 SQL 저장소에 쿼리 등록)

tx.commit() - 쓰기 지연 SQL 저장소 쿼리문 실행(실행 후 삭제), DB 저장

 

셋터 메서드를 통해 테이블 수정

. remove() 메서드를 통해 테이블 삭제

. find() 메서드를 통해 데이터 조회


단일 엔티티를 DB 테이블과 매핑하기

기본키 매핑

데이터베이스의 테이블에 기본키 설정은 필수라고 할 수 있습니다.

JPA에서는 기본적으로 @Id 애너테이션을 추가한 필드가 기본 키 칼럼이 되는데, JPA에서는 이러한 기본 키를 어떤 방식으로 생성해 줄지에 대한 다양한 전략을 지원합니다.

JPA에서 지원하는 기본키 생성 전략은 다음과 같습니다.

  • 기본키 직접 할당
    • 애플리케이션 코드 상에서 기본키를 직접 할당해주는 방식입니다.
    • 엔티티 클래스에서 @Id 애너테이션 추가
  • 기본키 자동 생성
    • IDENTITY
      • @Id 필드에 @GeneratedValue(strategy = GenerationType.IDENTITY) 추가
      • 기본키 생성을 데이터베이스에 위임하는 전략입니다.
      • 데이터베이스에서 기본키를 생성해 주는 대표적인 방식은 MySQL의 AUTO_INCREMENT 기능을 통해 자동 증가 숫자를 기본키로 사용하는 방식이 있습니다.
      • 엔티티 객체 생성 시 Id를 할당하지 않아도 됩니다. DB에서 대신 기본키 생성해 줌
    • SEQUENCE
      • @Id 필드에 @GeneratedValue(strategy = GenerationType.SEQUENCE) 추가
      • IDENTITY 방식과 똑같다. 별도의 기본키 전달 X
      • 데이터베이스에서 제공하는 시퀀스를 사용해서 기본키를 생성하는 전략입니다.
    • AUTO
      • @Id 필드에 @GenerationType(strategy = GenerationType.AUTO) 추가
      • JPA가 적절한 기본키 매핑 전략을 자동으로 선택해 준다.
      • 그냥 AUTO를 쓰면 되지 않을까??
기본키 매핑의 세가지 방법
em.persist(new Member(1L)); //기본키 직접 할당 - @Id
em.persist(new Member()); //기본키 자동 생성 - GenerationType.IDENTITY
em.persist(new Member()); // 기본키 자동 생성 - GenerationType.SEQUENCE

 

기본키 제외 나머지 필드와 DB 칼럼 간의 매핑 - @Column

엔티티 클래스에서 기본키를 제외한 나머지 필드들에도 테이블 칼럼과 매핑해 준다. 그러면 테이블과 매핑은 완성!

필드에 @Column 애너테이션 추가. 필드와 칼럼을 매핑해 준다. 얘가 없어도 자동으로 테이블 칼럼과 매핑되는 필드라고

간주해 주고 디폴트값을 적용해주긴 하지만! 최소한 @Column을 통해 nullable = false를 작성은 해주자

    @Column(nullable = false, updatable = false, unique = true)
    private String email;
  • 애트리뷰트
    • nullable
      • 칼럼에 null 값을 허용할지 여부를 지정합니다.
      • 디폴트 값은 true입니다.
    • updatable
      • 컬럼 데이터를 수정할 수 있는지 여부를 지정합니다.
      • 디폴트 값은 true입니다.
      • 여기서는 email 주소가 사용자 ID 역할을 한다고 가정하고 한번 등록되면 수정이 불가능하도록 하기 위해서 updatable 값을 false로 지정했습니다.
    • unique
      • 하나의 컬럼에 unique 유니크 제약 조건을 설정합니다.
      • 디폴트 값은 false입니다.
      • email의 경우 고유한 값이어야 하므로 unique 값을 true로 지정했습니다.
    • length
      • 컬럼에 저장할 수 있는 문자 길이
      • 디폴트 값은 255입니다.
    • name
      • 생략하면 엔티티 클래스의 필드명으로 칼럼이 생성되지만
      • name 어트리뷰트로 별도의 이름을 지정해서 엔티티 클래스의 필드명과 다른 이름으로 컬럼 생성 가능

@Enumerated(EnumType.STRING)

enum???

 


(중요) 엔티티 간의 연관 관계 매핑하기

연관관계 매핑이란?

방금 전까지는 하나의 엔티티 객체와 DB의 테이블과 매핑했다. 이제 엔티티 간의 관계를 맺어준다.

Spring data JDBC에서 학습했듯이, 엔티티 클래스 간의 관계를 만들어주는 것이 연관관계 매핑이다.

기본키, 외래키로 관계를 맺는 DB 테이블과는 다르게 엔티티 클래스 객체 참조를 통해 서로 관계를 맺는다. ➡ 어렵다

  • 단방향 연관관계
  • 양방향 연관관계

단방향 연관관계

  • 일대다 연관관계
  • 다대일 연관관계

단방향 연관관계

한쪽 클래스만 다른 쪽 클래스의 참조 정보를 가지고 있는 관계를 단방향 연관관계라고 한다.

여기서는 1 엔티티가 多에 해당하는 엔티티 객체 원소로 가지는 List 객체를 가지고 있다.

멤버 하나에 Order 여러 개!

Member는 Order를 알지만

Order는 Member를 모른다

단방향 연관관계라고 한다.

 

단방향 연관관계(2)

Order 클래스가 1 Member 객체를 가지고 있다.

Order 객체당 Member 객체 하나! 

Order는 Member를 알지만

Member는 Order를 모른다.

 

 

일대다 / 다대일 연관관계

일대다 연관관계

일대다 단방향 매핑방식은 잘 쓰이지 않는다.

 

 

다대일(다➡일) 단방향

에 해당하는 Order 클래스가 일에 해당하는 Member 객체를 외래키처럼 가지고 있습니다.

제일 기본적으로 쓰이는 매핑방식입니다.


양방향 연관관계

양방향 연관관계

양쪽 클래스가 서로의 참조 정보를 가지고 있는 관계를 양방향 연관 관계라고 한다.

한쪽은 객체정보가 포함된 List 객체를 

한쪽은 객체를

1 :   = Order 객체를 포함하고 있는 List를 객체를 가진다 : Member 객체를 가진다.

 

 

다대일(다➡일) 단방향

다에 해당하는 Order 클래스가 일에 해당하는 Member 객체를 외래키처럼 가지고 있습니다.

제일 기본적으로 쓰이는 매핑방식입니다.

 

코드로 보는 연관관계 매핑

1. 다대일 단방향 연관관계 매핑

다(Many)에 해당하는 Order 클래스

@Entity(name = "ORDERS")

@ManyToOne //다대일
@JoinColumn(name = "MEMBER_ID") //외래키에 해당하는 컬럼명을 작성

다대일 매핑을 위해선

  • @ManyToOne
  • @JoinColumn ⬅ Orders 테이블에서 외래키에 해당하는 칼럼명을 적는다. 

을 이용한다.

단방향이기 때문에 多쪽에서만 연결해 주면 매핑 작업 완료!

 

 

2. 다대일 매핑 + 일대다 매핑

 

Order 테이블 다대일 매핑을 통해 어떤 member가 주문했는지 알 수 있다.

그런데 member 입장에서 내가 어떤 주문을 했는지는 위의 다대일 매핑만으로는 알 수 없다.

이때 다대일 매핑이 된 상태에서 일대다 매핑을 추가해 양방향 관계를 만들어준다.

 

다대일(일반적) + 일대다 ➡ 양방향 매핑

 

일(1)에 해당하는 Member 클래스에 일대다 매핑

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<>();

mapped by 관계를 소유하고 있는 필드를 지정하는 것

 

에 해당하는 클래스에서 외래키 역할을 하는 필드를 mappedBy 값으로 넣어준다.