2022년 5월 10일 TIL

yshjft·2022년 5월 10일
0

데브코스 TIL

목록 보기
30/45
post-thumbnail

EntityManagerFactory, EntityManager

EntityManagerFactory

  • 하나만 생성된 이후 요청 마다 EntityManager를 생성한다.
  • Thread Safe하다.

EntityManager

  • Entity를 관리(CRUD)한다.
  • Thread Safe하지 않다. 동시성 이슈가 발생할 수 있다.
    • 쓰고 버려라!
  • 내부적으로 DB connection을 얻어 DB에 접근한다.

Entity

  • RDB의 Table과 매핑되는 객체이다.
  • 기본 생성자가 반드시 있어야 한다.

영속성 컨텍스트(Persistence Context)

  • entity를 영구 저장하는 환경
  • EntityManagerEntity를 영속성 컨텍스트에 보관하고 관리한다.
  • persist() == 영속화를 시키겠다 == 영속성 컨텍스트에서 관리하겠다

영속성 컨텍스트의 특징

영속성 컨텍스트 안에서 관리되는 엔티티는 식별자 값을 반드시 가져야한다.

1차 캐시에서 엔티티는 key(식별자)-value(Entity)로 관리되기 때문에 반드시 식별자(ID, PK)가 있어야 한다.

영속성 컨텍스트와 데이터 베이스 저장

  • JPA는 트랜잭션을 커밋(transaction.commit())하는 순간 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영한다(FLUSH).
  • FLUSH는 영속성 컨텍스트의 변경 내용을 DB에 동기화 하는 것이다. 이 때 등록, 수정, 삭제된 Entity가 DB에 반영된다.

Entity 생명 주기

상태

  • 비영속(new/transient)
    영속성 컨텍스트에 전혀 관계가 없는 상태

  • 영속(managed)

  • 영속성 컨텍스트에 저장된 상태
  • 영속 상태의 Entity 객체
    • Entity 변경 등 모든 것을 영속성 컨텍스트에서 감지하며 트랜잭션에서 관리되다 commit을 통해 DB에 모두 FLUSH 한다.
  • 준영속(detached)
  • 영속성 컨텍스트에서 저장되었다가 분리된 상태
  • 한번이라도 영속성 컨텍스트에서 관리된적이 있는 경우(id가 있는 객체)
  • 삭제(removed)
    삭제된 상태

메서드

EntityManger에 의해서 호출되는 함수

  • persist(): 영속성 컨텍스트에서 관리되도록 한다.
  • detach(): 영속성 컨텍스트에서 더 이상 관리되지 않도록 한다.
  • clear(): 영속성 컨텍스트에서 관리되는 Entity들을 모두 지워버려 모든 준영속 상태로 만든다.
  • close(): 영속성 컨텍스트를 종료하여 모든 Entity들을 준영속 상태로 만든다.
  • merge(): 모든 Entity들이 준영속 상태에서 영속 상태로 만든다.
  • remove(): 영속성 컨텍스트에서 분리하고 DB에서도 삭제한다.

영속성 컨텍스트 이해

JPA의 모든 변경은 트랜잭션 안에서 일어나야 한다.

저장

Entity를 영속성 컨텍스트에서 관리하기 위해 항상 트랜잭션을 시작하고 끝내야한다.

  1. transaction.begin()
  2. em.persist(Entity)
    • 쓰기 지연 저장소에 쿼리(INSERT INTO ~ ) 저장
    • 1차 캐시에 Entity를 key-value로 저장
  3. transaction.commit()
    • em.flush()가 호출되어 쓰기 지연 저장소에 쿼리를 수행하여 DB와 Entity를 동기환한다.

조회

1차 캐시 이용

1차 캐시에 데이터가 남아 있다면 DB에 질의하지 않고 1차 캐시에 저장된 데이터를 가지고 온다.
tx.commit()은 FLUSH를 하는 것이기에 1차 캐시 이용이 가능하다.
1차 캐시에 관하여 참고

DB 이용

  1. find()
  2. 1차 캐시 조회
  3. DB 조회
  4. DB 조회 결과 1차 캐시에 저장
  5. 반환

수정

  • 변경 감지(Dirty Checking)
    변경 감지는 영속 상태의 엔티티에만 적용된다.

    1. trasaction.commit()을 통해 em.flush() 발생
    2. 엔티티와 스냅샷(초기값) 비교
    3. 변경된 내용이 있을 경우 update Query를 수행한다. 변경된 내용이 없으면 수행하지 않는다.
  • 참고)merge()에 대하여
    Spring Data JPA에서 save()를 할 때 엔티티에 ID가 있으면 생성이 아니라 수정으로 보고 em.merge(entity)를 수행하게 된다. 문제는 모든 속성이 변경되기 때문에 null값 업데이트의 가능성이 있다. 따라서 수정시에는 Dirty Checking이 권장된다.

    1. merge() 실행
    2. 1차 캐시에서 엔티티 조회한다. 만약 없으면 DB에서 조회하고 1차 캐시에 저장한다.
    3. 조회된 영속 엔티티에 준영속 엔티티의 값을 채워 넣는다.
    4. 영속 상태인 엔티티를 반환한다.
      • 반드시 반환된 엔티티를 사용해야한다. 파라미터로 전달된 엔티티는 여전히 영속성 컨텍스트에서 관리되고 있지 않다.

삭제

  1. em.remove(entity)
  2. transaction.commit()로 인해 em.flush발생하고 DELETE ... 쿼리를 날린다.

추가)

기본적으로 EntityManager를 생성할 때 영속성 컨텍스트가 하나 만들어진다. 하지만 Spring 같은 컨테이너 환경의 JPA에서는 여러 EntityManager가 하나의 영속성 컨텍스트를 공유한다.

스프링 컨테이너는 트랜잭션 범위의 영속성 컨텍스트 전략을 기본으로 사용한다. 이 전략은 트랜잭션이 시작할 때 영속성 컨텍스트를 생성하고 트랜잭션이 끝날 때 영속성 컨텍스트를 끝낸다.

트랜잭션이 같으면 같은 영속성 컨텍스트를 사용한다.

트랜잭션 범위의 한 클래스에서 repo1 의 어떤 em.A() 수행하는 것과, repo2 의 em.B() 를 수행하는 것은 둘 다 똑같은 영속성 컨텍스트를 사용하는 것이다.

트랜잭션이 다르면 다른 영속성 컨텍스트를 사용한다

Transaction 범위의 A 클래스, Transaction 범위의 B 클래스가 한 repo의 em.A()를 동시에 사용해도 트랜잭션에 따라 접근하는 컨텍스트가 다르다.

proxy

@Service
public class MovieService {

    @Autowired
    private EntityManager entityManager;

    ...
}

스프링은 싱글톤 기반으로 동작 하기에 위와 같이 EntityManager를 주입 받았다면, 여러 스레드가 같은 EntityManager에 접근을 하여 스레드 세이프 하지 않다고 생각될 수 있으나 실제로는 컨테이너에서 EntityManager 대신 proxy를 주입한다. 그리고 이 proxy는 EntityManager를 생성하여 thread safe를 보장할 수 있다.

엔티티매핑

@Entity, @Table

  • @Entity
    객체가 영속성 컨텍스트에서 관리될 수 있음을 나타낸다.

    • name
      엔티티 이름을 설정
  • @Table

    @Table(name = "member")

    name을 이용하여 table 이름을 설정

AUTO DDL

  • create

    • DROP + CREATE
    • 개발 환경, 테스트 환경에서 사용
  • create-drop

    • DROP + CREATE + DROP
    • 하나의 테스트 시작하고 다른 테스트에 영향을 주지 않기 위해 사용
    • 개발 환경, 테스트 환경에서 사용
  • update

    • 테이블, 엔티티 매핑 정보를 비교해
    • 개발 환경, 테스트 환경에서 사용
  • validate

    • 테이블, 엔티티 매핑정보를 비교해서 차이가 있으면 어플리케이션을 실행하지 않는다.
    • 런타임 시점 오류 방지를 위해 사용
  • none

    • 자동 생성 기능을 사용하지 않는다.

spring.jpa.hibernate.ddl-auto

JPA auto configuration 사용시 AUTO DDL 옵션을 위 항목에 작성해야한다.

spring.jpa.properties.hibernate.hb2ddl.auto

JPA auto configuration을 사용하지 않고 직접 Bean으로 설정하면서 JpaProperties를 사용하는 경우 AUTO DDL 옵션을 위 항목에 작성해야한다.

참고)

사실 이에 대한 내용을 어제 TIL에서 작성하였다...

@Column

구지 명시를 해야 할까?라는 생각이 들 수도 있지만 명시를 해 놓으면 RDB 테이블의 모습을 개발자가 쉽게 유추할 수 있기 때문에 명시를 권장한다!

name

  • 필드와 매핑할 컬럼 이름
  • name을 명시하지 않으면 기본적으로 필드명을 따라가게된다.
    • Camel Case → underscore (ex. nickName → nick_name)

nullable(DDL)

NOT NULL option

unique(DDL)

unique 조건

length(DDL)

varchar

insertable, updatable

  • insertable
    RDB에 insert 안함

  • updatable
    RDB에 update 안함

  • false 이면 컬럼에 조작을 가하지 않겠다.

  • 자주 사용되지는 않는다.

기본키 매핑 전략

@Id

  • 직접 할당
    • 아무것도 설정 안함
    • 개발자가 코드로 임의로 정한다
  • SEQUENCE
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    • 데이터베이스 시퀀스에서 식별자 값을 획득한 후 영속화
  • TABLE
    • 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속화
    • 자주 사용되는 것은 아니니 이러한 전략이 있다고만 알자
  • IDENTITY
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    • 데이터베이스 엔티티를 저장해서 식별자 값을 획득한 후 영속화
    • em.persist() 시점에 INSERT 쿼리가 수행된다.
  • AUTO
    @GeneratedValue(strategy = GenerationType.AUTO)
    @GeneratedValue
    • 업계에서는 AUTO를 가장 많이 사용
    • 데이터 베이스 방언(dialect)에 따라서 자동으로 전략을 선택
    • 참고) hibernate.dialect
      어떤 RDB를 사용해도 dialect에 맞게 쿼리를 수행하도록 한다.

기타 컬럼 매핑

columnDefinition=“TIMESTAMP”

@Column(name = "order_datetime", columnDefinition = "TIMESTAMP")

TIMESTAMP값이 저장되어 진다.

@Enumerated(EnumType.STRING)

enum값 저장을 원하는 경우

@Lob

  • 아주 긴 텍스트를 넣고 싶을 때
  • Long text

데이터 중심 설계의 문제점

객체 그래프 탐색이 불가능하다.

profile
꾸준히 나아가자 🐢

0개의 댓글