[SEB BE] Section 3. JPA(Java Persistence API)?

박두팔이·2023년 2월 23일
0

스프링프레임워크

목록 보기
9/18

학습목표

  1. JPA는 자바 진영에서 사용하는 ORM(Object-Relational Mapping)기술의 표준 사양(명세)이다.
  2. 하이버네이트(Hibernate) ORM은 JPA에서 정의해둔 인터페이스를 구현한 구현체이다.
  3. JPA에서 지원하는 기능 외에 하이버네이트 자체적으로 사용할 수 있는 API를 지원한다.
  4. JPA는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트에 보관하여 애플리케이션 내에서 오래 지속시킨다.
  5. 영속성 컨텍스트 관련 JPA API
  • em.persist()를 사용해서 엔티티 객체를 영속성 컨텍스트에 저장할 수 있다.
  • 엔티티 객체의 setter 메서드를 사용해서 영속성 컨텍스트에 저장된 엔티티 객체의 정보를 업데이트 할 수 있다.
  • em.remove()를 사용해서 엔티티 객체를 영속성 컨텍스트에서 제거할 수 있다.
  • em.flush()를 사용해서 영속성 컨텍스트의 변경 사항을 테이블에 반영할 수 있다.
  • tx.commit()을 호출하면 내부적으로 em.flush()가 호출된다.

JPA란?

JPA는 자바의 인터페이스로 정의되어 있는 ORM기술의 표준 사양이다. 따라서, JPA를 구현한 구현체는 따로 존재한다.


Hibernate ORM

JPA표준사양을 구현한 구현체가 하이버네이터이다.

이 외 EclipseLink, DataNucleus 등이 있지만 설명은 생략한다.

하이버네이터 ORM은 JPA에서 정의해둔 인터페이스를 구현한 구현체로써 JPA의 지원기능 이외에 자체적으로 사용할 수 있는 API도 지원하고 있다.


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

JPA는 서버의 데이터 액세스 계층 상단에 위치한다.

데이터를 저장, 조회 등 작업은 JPA를 거쳐 JPA의 구현체인 하이버네이터 ORM을 통해 JDBC API를 이용하여 데이터베이스에 접근한다.


영속성 컨텍스트(Persistence Context)

Persistence란 영속성, 지속성이라는 뜻이다. 즉, 사라지지 않고 오래 지속되게 한다라는 의미를 가지고있다.
ORM은 객체와 데이터베이스 테이블을 매핑하여 엔티티 클래스 객체안에 있는 정보를 테이블에 저장하는 기술이다.

💡JPA에서의 영속성 컨텍스트란?

영속성 컨텍스트에 데이터베이스의 테이블과 매핑되는 엔티티 객체 정보를 보관하여 애플리케이션 내에서 오래 지속되도록 하는 것이다.

보관된 엔티티 정보는 데이터베이스 테이블에 데이터를 저장, 수정, 조회, 삭제하는데 사용된다.

영속성 컨텍스트에는 1차캐시영역과 쓰기 지연 SQL저장소 라는 영역이 있다.

API를 사용하여 영속성 컨텍스트에 엔티티(개체, 데이터)를 저장하면 1차캐시에 정보가 저장된다.

JPA에서 지원하는 API를 사용하여 영속성 컨텍스트에 엔티티를 저장하는 방법

  • 1) build.gradle 설정
dependencies{
  	implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // (1)
}
  • 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 쿼리 출력
  • 3) @Entity, @Id, @GeneratedValue 애너테이션

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

@GeneratedValue
데이터베이스 테이블에서 애너테이션이 붙은 해당 변수를 기본키가 되는 식별자로 자동 설정해준다.

  • 4) JPA의 영속성 컨텍스트는 EntityManager 클래스에 의해 관리된다. EntityManager 클래스 객체는 EntityManagerFactory객체를 Spring 으로부터 DI받을 수 있다.
@Configuration
public class JpaBasicConfig {
    private EntityManager em;
    

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

        return args -> {
            example01();
        };
    }
  • 5) EntityManagerFactory의 createEntityManager() 메서드를 이용해서 EntityManager 클래스의 객체를 얻을 수 있다.

  • 6) em.persist(member) 메서드 호출을 통해 영속성 컨텍스트에 member객체의 정보들 저장.

em.persist(member);

em.persist(member)를 호출하면 1차 캐시에 member객체가 저장되고 이 member 객체는 쓰기 지연 SQL 저장소에 INSERT 쿼리 형태로 등록이 된다.

  • 7) find()메서드를 통해 영속성 컨텍스트에 member객체 저장되어있는지 확인
 Member resultMember = em.find(Member.class, 1L);
 
 // 첫 번째 파라미터 mamber.class = 조회 할 엔티티 클래스 타입
 // 두 번째 파라미터 1L = 조회 할 엔티티 클래스의 식별자 값

영속성 컨텍스트에 있는 데이터를 실제 테이블로 저장하는 방법

this.tx = em.getTransaction();
  • 1) EntityManager를 통해서 Transaction 객체를 얻은 후 이 객체를 기준으로 데이터베이스의 테이블에 데이터를 저장한다.

  • 2) Transaction을 시작하기 위해서는 .begin()메서드를 먼저 호출해주어야 한다.

  • 3) .persist()메서드를 통해서 객체를 영속성 컨텍스트에 저장한다.

 em.persist(member);
  • 4) .commit()메서드를 호출하는 시점에 영속성 컨텍스트에 저장되어 있는 member객체를 데이터베이스의 테이블에 저장한다.

즉, tx.commit(); 으로 트랜잭션을 커밋하여 데이터베이스에 변경 내용을 반영한다.

tx.commit();
  • 5) em.find(Member.class, 1L)을 호출하면 .persist()메서드를 통해 영속성 컨텍스트에 저장했던 멤버 객체를 1차 캐시에서 조회한다. 1차 캐시에 member객체 정보가 있기 때문에 별도로 테이블에 SELECT쿼리를 전송하지 않는다.

  • 6) em.find(Member.class, 2L)를 호출해서 식별자 값이 2L인 member 객체를 조회한다. 하지만 영속성 컨텍스트에는 식별자 값이 2L인 member객체는 존재하지 않는다 따라서 resultMember2 == null의 결과는 true가 된다.

  • 7) 영속성 컨텍스트에서 식별자 값이 2L인 member객체가 존재하지 않기 때문에 테이블에 직접 SELECT쿼리를 전송한다.

⭐️ 중요하게 기억해야 할것!!

  • em.persist()를 호출하면 영속성 컨텍스트의 1차 캐시에 엔티티 클래스의 객체가 저장되고, 쓰기 지연 SQL저장소에 INSERT쿼리가 등록된다.
  • tx.commit() 을 하는 순간 쓰기 지연 SQL저장소에 등록된 INSERT쿼리가 실행되고, 실행된 INSERT쿼리는 쓰기 지연 SQL저장소에서 제거된다.
  • em.find()를 호출하면 먼저 1차 캐시에서 해당객체가 있는지 조회한 뒤, 없으면 테이블에 SELECT쿼리를 전송해서 조회한다.

쓰기지연을 통한 연속성 컨텍스트상태와 테이블에 엔티티(데이터) 일괄 저장하는 방법

위의 그림은 tx.commit()메서드가 실행되기 직전의 영속성 컨텍스트 상태를 표현한 것이다. tx.commit()을 해야만 em.persist()를 통해 쓰기 지연 SQL저장소에 등록된 INSERT쿼리가 실행된다.

즉, tx.commit() 통해 쿼리를 실행시켜야 테이블에 데이터가 저장된다는 의미이다.

위의 그림은 tx.commit()후의 영속성 컨텍스트 상태이다. tx.commit()이 실행되면 쓰기 지연 SQL저장소에 등록된 INSERT쿼리가 모두 실행되고 실행된 커리는 제거된다. -> 테이블에 데이터 저장됨.


테이블에 이미 저장된 데이터를 JPA를 이용하여 영속성컨텍스트와 테이블에 엔티티 업데이트하는 방법

@Configuration
public class JpaBasicConfig {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
        this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();

        return args -> {
            example04();
        };
    }

    private void example04(){
        tx.begin();
        em.persist(new Member("hgd1@gmail.com"));
        tx.commit();

        tx.begin();
        Member member1 = em.find(Member.class,1L);
        member1.setEmail("hgd1@yahoo.co.kr");
        tx.commit();
    }
}

실행과정은 이렇다.
테이블에 저장된 member객체를 조회할 때는 테이블이 아닌 영속성 컨텍스트의 1차 캐시에서 조회를 한다.
영속성 컨텍스트의 1차 캐시에 이미 저장된 객체가 있기 때문에 영속성 컨텍스트에서 조회한다는 사실을 기억해야한다.

JPA API에 update()가 있을 것 같지만 setter()로 값을 변경하기만 하면 업데이트 로직은 .commit()이 실행될 때 쓰기 지연 SQL저장소에 등록된 UPDATE쿼리가 실행된다.

삭제는 위의 코드에서 setter()대신 remove()를 사용하면 된다.

profile
기억을 위한 기록 :>

0개의 댓글