woowacourse-study / 2022-jpa-study

🔥 우아한테크코스 4기 JPA 스터디 (22.06.13~22.07.02) 🔥
5 stars 1 forks source link

[섹션 1, 섹션 2, 섹션 3] 어썸오 제출합니당 #3

Closed awesomeo184 closed 2 years ago

awesomeo184 commented 2 years ago

데이터베이스 방언

JPA는 특정 데이터베이스에 종속되지 않는다. 각각의 RDBMS 제품은 SQL 문법과 함수가 조금씩 다름.

ex) MySQL - VARCHAR, LIMIT, Oracle - VARCHAR2, ROWNUM 등

dialect 속성에 DB 제품을 명시해주면 해당 DB의 문법으로 쿼리 매핑 (Dialect 인터페이스에 접근하면 각 DB의 Dialect 구현체 사용)

<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>

JPA 구동 방식

❗️ 주의점

JPQL(Java Persistence Query Language)

단건 조회는 다음과 같은 코드로 간단하게 처리할 수 있다.

Member findMember = em.find(Member.class , id);

그런데 하나 이상의 목록을 여러 조건을 붙여서 검색하려면 어떻게 해야할까?

JPA는 엔티티 객체를 중심으로 개발하므로 검색을 할 때도 테이블이 아닌 엔티티 객체를 대상으로 검색해야한다.

애플리케이션이 필요한 데이터만 데이터베이스에서 불러오려면 결국 검색 조건이 포함된 SQL을 사용해야 하는데, JPA는 JPQL이라는 쿼리 언어로 이런 문제를 해결한다.

TypedQuery<Member> query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();

JPQL은 SQL을 추상화한 객체지향 쿼리 언어이다. SQL과 문법이 거의 유사하지만 큰 차이가 있다.

SQL은 데이터베이스 테이블을 대상으로 쿼리하지만, JPQL은 엔티티 객체를 대상으로 쿼리한다. 위 예제의 select m from Member m에서 from Member는 엔티티 객체를 의미하는 것이지 데이터베이스 테이블을 의미하는 것이 아니다. JPQL은 데이터베이스 테이블을 전혀 알지 못한다. 또한 JPQL은 SQL과 달리 대소문자를 엄격히 구분한다.


EntityManager

persistence 설정 정보를 읽어서 EntityManagerFactory 를 생성한다. EntityManagerFactory는 EntityManager를 생성한다.

EntityManager는 DB와 관련된 작업을 수행하기 위한 인터페이스. 이 EntityManager를 통해서 객체를 조회하고 저장하는 등의 작업을 할 수 있다.

PersistenceContext

Java Program과 DB 사이의 중간 계층. 개념적으로는 엔티티를 영구 저장하는 환경을 말한다.

다음과 같은 이점을 제공한다.

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Member member = new Member(1L, "hello");
        em.persist(member);

        Member findMember1 = em.find(Member.class, 1L);
        Member findMember2 = em.find(Member.class, 1L); // 같은 객체를 가리키고 있다.

        tx.commit();
        em.close();
        emf.close();
    }
}

EntityManager를 통해 PersistenceContext에 접근할 수 있다.

EntityManager와 PersistenceContext

기본적으로…

SessionImpl이라는 구현체가 EntityManager를 구현하고 있음.

SessionImpl은 PersistenceContext라는 타입을 멤버로 가지고 있다.

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        Member hello1 = new Member(1L, "hello1");
        Member hello2 = new Member(2L, "hello2");
        em.persist(hello1);
        em.persist(hello2); // 여기 브레이크 포인트를 찍으면

        tx.commit();
        em.close();
        emf.close();
    }
}

https://user-images.githubusercontent.com/63030569/173238583-2976fdbe-5aba-411e-a411-c024e9e1e1b4.png

위처럼 persistenceContext안에 있는 해시맵(entitiesByKey) 안에 “hello1”이라는 이름을 가진 멤버 객체를 저장하고 있는 것을 확인할 수 있다.

플러시(flush)

플러시(flush())는 영속성 컨텍스트의 변경사항을 데이터베이스에 반영한다.

플러시를 실행하면 구체적으로 다음과 같은 일이 일어난다.

  1. 영속성 컨텍스트 내 모든 엔티티를 스냅샷과 비교해 변경된 엔티티를 찾는다.
  2. 수정된 엔티티가 있다면 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록한다.
  3. 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.

플러시는 다음과 같은 상황에서 호출된다.

  1. 직접 호출(em.flush())
  2. 트랜잭션 커밋 시
  3. JPQL 쿼리 실행 시

JPQL 쿼리를 실행할 때 플러시가 호출되는 이유는 다음과 같다.

em.persist(memberA);
em.persist(memberB);
em.persist(memberC);

query = em.createQuery("select m from Member m", Member.class);
List<Member> members = query.getResultList();

memberA, B, C는 영속성 컨텍스트에는 있지만 아직 데이터베이스에 반영되지 않았다. 근데 여기서 조회 쿼리를 실행하면 당연히 memberA, B, C를 찾을 수 없다. 그렇기 때문에 JPQL을 실행하면 그 전에 플러시가 자동 호출되도록 설계된 것이다.

플러시 모드를 직접 지정하려면 javax.persistence.FlushModeType을 사용한다.

EntityStatus

image

ex) saveEvent에서 엔티티 상태에 따른 처리

image

merge

준영속 상태의 엔티티를 영속 상태로 만든다. 정확히 말하면 새로운 영속 상태의 엔티티를 반환한다.

병합과정