[7/25 TIL] JPA(영속성 컨텍스트, 실습, 단일엔티티매핑)

yumyeonghan·2023년 7월 26일
0

🍃프로그래머스 백엔드 데브코스 4기 교육과정을 듣고 정리한 글입니다.🍃

영속성 컨텍스트

EntityManagerFactory

그림 출처

  • Entity를 관리하는 EntityManager를 생산하는 객체
  • Thread Safe

EntityManager

  • Entity를 저장, 수정, 삭제, 조회하는 Entity와 관련된 모든 일을 처리
  • Thread Safe 하지 않기 때문에 여러 Thread에서 동시에 접근하면 동시성 이슈가 발생

영속성 컨텍스트

그림 출처

  • Entity를 영구 저장하는 환경
  • EntityManager는 Entity를 영속성 컨텍스트에서 보관하고 관리
  • 영속성 컨텍스트와 식별자 값
    • 영속성 컨텍스트 안에서 관리되는 엔티티는 식별자 값을 반드시 가져야 한다.
    • key-value로 엔티티를 관리한다.
    • key에 pk(id)값이 아니라, 다른 식별자(name)로 관리할 수 있다.
  • 영속성 컨텍스트와 데이터베이스 저장
    • JPA는 트랜잭션을 커밋하는 순간 flush를 통해 영속성 컨텍스트에 새로 저장된 엔티티를 DB에 반영한다.
    • flush는 영속성 컨텍스트의 변경 내용을 DB에 동기화하는 작업인데, 이때 등록, 수정, 삭제한 엔티티를 DB에 반영한다.
  • 영속성 컨텍스트가 엔티티를 관리함으로 얻는 이점
    • 1차 캐치
    • 동일성 보장
    • 트랜잭션을 지원하는 쓰기 지연
    • 변경 감지
    • 지연 로딩

엔티티 생명주기

  • 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
// 객체가 영속성컨텍스트, 데이트베이스와 무관한 상태이다.
Customer customer = new Customer(); 
customer.setFirstName("myeonghan");
customer.setLastName("yu");

  • 영속(managed): 영속성 컨텍스트에 저장된 상태
Customer customer = new Customer(); 
customer.setFirstName("myeonghan");
customer.setLastName("yu");

// customer객체가 영속성 컨텍스트에서 관리된다.
em.persist(customer);

  • 준영속(detached): 영속성 컨텍스트에 저장되었다가 분리된 상태
// 영속상태의 customer객체(엔티티)를 영속성컨텍스트에서 분리한다.
em.detach(customer);
// 영속상태의 모든 객체를 영속성컨텍스트에서 분리한다.
em.clear()
// 영속성컨텍스트를 종료한다.
em.close()

  • 삭제(removed): 삭제된 상태
// customer 엔티티를 영속성컨텍스트에서 분리하고, DB에서도 삭제한다.
em.remove(customer)

영속성 컨텍스트 실습

저장

		EntityManager em = emf.createEntityManager(); //엔티티 매니저 생성
        EntityTransaction transaction = em.getTransaction(); //트랜잭션 획득
        transaction.begin(); //트랜잭션 시작
        Customer customer = new Customer("명한", "유"); //비영속
        em.persist(customer); //영속화
        transaction.commit(); //트랜잭션 commit, 트랜잭션 커밋 되는 순간 flush가 되어 쿼리가 수행되면서 DB와 동기화 된다.
  • customer가 영속화 되면서 쓰기 지연 저장소에 insert 쿼리가 저장된다.
  • customer가 영속화 되면서 1차 캐시에 key(@id)-value(Entity) 형식으로 저장된다.
  • 트랜잭션 커밋 시점에 쓰기 지연 쿼리가 수행된다.

1차 캐시를 이용한 조회

 		EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
        transaction.begin();
        Customer customer = new Customer("명한", "유");
        em.persist(customer);
        transaction.commit();

        Customer foundCustomer = em.find(Customer.class, 1L); //1차 캐시에서 조회한다.
  • 영속성 컨텍스의 1차 캐시에 customer가 있기 때문에 DB에 질의하지 않고 1차 캐시에서 바로 결과를 반환한다.

DB를 이용한 조회

		EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
		transaction.begin();
        Customer customer = new Customer("명한", "유");
        em.persist(customer);
        transaction.commit();

        em.clear(); //영속성 컨텍스트를 초기화 한다.
        Customer foundCustomer = em.find(Customer.class, 1L); //DB에서 조회한다.
  • 영속성 컨텍스트를 초기화 해서 1차 캐시에 customer가 없기 때문에 DB에 직접 질의를 해서 결과를 반환한다.

수정

		EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
		transaction.begin();
        Customer customer = new Customer("명한", "유");
        em.persist(customer); //스냅샷 처리
        transaction.commit(); //엔티티를 영속화한후, 커밋을해서 DB에 저장
 
 		transaction.begin();
        //update() 없이, 객체를 수정하기만 하면 DB에서 수정됨
        customer.changeLastName("박");
        customer.changeFirstName("은지");
        transaction.commit(); //스냅샷과 비교해서 변경사항이 있는지 확인
  • JPA는 엔티티를 영속성 컨텍스트에 보관할 때, 최초 상태를 복사해서 저장해 두는데 이것을 스냅샷이라 한다.
  • flush 시점에 스냅샷과 엔티티를 비교해서 변경된 엔티티를 찾고, 이에 대한 Update Query를 수행한다.
  • 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용이 된다.

삭제

		EntityManager em = emf.createEntityManager();
        EntityTransaction transaction = em.getTransaction();
		transaction.begin();
        Customer customer = new Customer("명한", "유");
        em.persist(customer);
        transaction.commit();

        transaction.begin();
        em.remove(customer);
        transaction.commit(); // customer 엔티티를 영속성 컨텍스트에서 분리하고, DB에서도 삭제한다.
  • 트랜잭션 커밋 시점에 Delete Query가 수행된다.

단일엔티티매핑

@Entity
@Table(name = "customers")
public class Customer extends BaseEntity {

    @Id @GeneratedValue
    private Long id;

    @Column(length = 5, nullable = false)
    private String firstName;

    @Column(length = 2, nullable = false)
    private String lastName;
    
    @Enumerated(EnumType.STRING)
    private CustomerStatus customerStatus;

    @Lob
    private String memo;

    public Customer() {
    }
}
  • JPA는 엔티티 객체를 생성할 때, 기본 생성자를 사용한다.
    • 왜? reflection API 사용하기 때문에

@Entity

  • JPA에서 사용할 엔티티를 지정

@Table

  • name: 매핑할 테이블 이름 설정

AUTO DDL 옵션

  • create: 기존 테이블을 삭제하고 새로 테이블을 생성
  • create-drop: 어플리케이션 종료시 생생한 DDL 제거
  • update: 테이블, 엔티티 매핑정보를 비교하여 변경사항 수정
  • validate: 테이블, 엔티티 매핑정보를 비교해서 차이가 있으면 어플리케이션을 실행 안함
  • none: 자동 생성 기능을 사용 안함

@Column

  • name: 필드와 매핑할 테이블의 컬럼 이름
  • nullable(DDL): null 값 허용 여부 설정
  • unique(DDL): 해당 컬럼의 유니크 제약조건 추가
  • length(DDL): 문자 길이 제약조건으로 사용 (String 타입에만 사용)

기본키 매핑전략

  • @Id만 사용해서 직접 할당
  • @Id와 @GeneratedValue를 통해 자동 할당
  • @GeneratedValue 종류
    • SEQUENCE
      • 데이터베이스 시퀀스에서 식별자 값을 획득(조회)한 후 영속화
      • ORACLE, H2
    • TABLE
      • 데이터베이스 시퀀스 생성용 테이블에서 식별자 값을 획득한 후 영속화
    • IDENTITY
      • 데이터베이스에 엔티티를 저장해서 식별자 값을 획득한 후 영속화
      • 엔티티가 영속화 되려면 식별자 값이 반드시 필요하기 때문에 em.persist() 시점에 INSERT 쿼리가 수행됨
      • Mysql (AUTO_INCREMENT)
    • AUTO
      • 데이터 베이스 방언(dialect)에 따라서 자동으로 전략 선택

기타 컬럼매핑

  • @Enumerated(EnumType.STRING)
    • Enum 타입의 객체를 문자열로 저장
  • @Lob
    • 대형 데이터를 저장할 때 사용
profile
웹 개발에 관심 있습니다.

0개의 댓글