[SPRING] Entity

야부엉·2023년 11월 7일
0

SPRING

목록 보기
2/45

1. Entity

1.Entity란?

  • JPA에서 관리되는 객체
  • Entity 클래스 DB의 테이블과 맵핑이 된다.

2. 사용 예시

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    @Id
    // auto - increment
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    // nullable: null 허용 여부
    // unique: 중복 허용 여부 (false 일때 중복 허용)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    // length: 컬럼 길이 지정
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}
  • @Entity : JPA가 관리할 수 있는 Entity 클래스 지정
    - @Entity(name = "이름") 이름을 지정 할 수 있다.(default는 클래스 이름)
    - @Entity 선언을 하면, 인스턴스화 할때 기본적으로 기본 생성자를 사용하기 때문에 기본 생성자가 있는지 체크!!

  • @Table : 맵핑할 테이블 지정
    - 위와 같이 이름 설정이 가능하고 default로는 entity명으로 맵핑

  • @Id : 테이블의 기본 키 설정
    - 영속성 컨텍스트에서 Entity를 구분하고 관리할 때 사용되는 식별자 역할을 수행 -> 없을시 오류 발생!!
    - @GeneratedValue 옵션을 추가하면 기본 키 생성을 DB에 위임

2. 영속성 컨텍스트

1. 영속성 컨텍스트란?

  • Entity 객체를 효율적으로 관리하기 위해 만들어진 공간
  • JPA를 통해 DB 관리가 가능해졌는데, 이러한 관리 과정을 효율적으로 처리하기 위해 JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통

2. Entity Manager

  • 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요하다
  • EntityManager를 사용해서 Entity 관리를 한다.
  • EntityManager는 EntityManagerFactory에서 생성

3. Entity Manager Factory

  • DB 하나에 하나만 생성되어 애플이케이션이 동작하는 동안 사용
  • Persistence.xml 설정을 통해 persistence가 EntityManagerFactory를 생성하고, 이 팩토리가 entityManager를 생성한다.
  • 사용 예시
// JPA는 persistence.xml 의 정보를 토대로 EntityManagerFactory를 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("memo");

// EntityManagerFactory를 사용하여 EntityManager를 생성
EntityManager em = emf.createEntityManager();

자바에서는 /resources/META-INF/ 위치에 persistence.xml 파일을 만들어 정보를 넣어두어 사용된다.

3. 트랜잭션

1. 트랜잭션이란?

  • 여러 개의 SQL을 하나의 트랜잭션으로 그룹화를 해서, 안에 있는 SQL이 모두 성공적으로 수행이 되면 변경을 영구적으로 반영, 단 하나라도 실패시 모든 변경이 되돌아간다.
  • DB의 데이터의 무결성과 정합성을 유지하기 위해 사용

2. JPA의 트랜잭션

  • JPA에서는 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연저장소(Action Queue)에 전부 가지고 있다가 마지막까지 정상적으로 수행시 commit을 통해 한번에 DB에 요청해 변경을 반영

3. 사용 예시

@Test
@DisplayName("EntityTransaction 성공 테스트")
void test1() {
    EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.

    try { // DB 작업을 수행합니다.

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setId(1L); // 식별자 값을 넣어줍니다.
        memo.setUsername("Robbie");
        memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
        // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
        //오류가 발생했을 때 트랜잭션의 작업을 모두 취소하고, 이전 상태로 되돌리는 명령어입니다.
    } finally {
        em.close(); // 사용한 EntityManager 를 종료합니다.
    }

    emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}

4. 영속성 컨텍스트의 기능

1. 1차 캐시

  • 영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있다.
  • 우리가 저정하는 Entity들도 캐시 저장소(1차 캐시)에 저장된다.
  • 캐시저장소는 맵의 구조
    - Key에는 @id로 맵핑한 기본키(식별자)
    - value에는 해당 entity의 객체
  • 장점
    1. DB 조회 횟수를 줄임
    2. DB row 1개 당 객체 1개가 사용되는 것을 보장 (객체 동일성 보장)
      	Memo memo1 = em.find(Memo.class, 1);
        Memo memo2 = em.find(Memo.class, 1);
        // true 결과를 반환
        System.out.println(memo1 == memo2);
        

2. Entity 저장

  • em.persist(memo); 메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장합니다.
  • 사용 예시
@Test
@DisplayName("1차 캐시 : Entity 저장")
void test1() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = new Memo();
        memo.setId(1L);
        memo.setUsername("Robbie");
        memo.setContents("1차 캐시 Entity 저장");

        em.persist(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}
  • em > persistenceContext > entitiesBykey를 확인해 보시면key-value 형태로 정보가 저장되었음을 확인 할 수 있다.

3. Entity 조회

  1. 캐시 저장소에 조회하는 ID가 존재하지 않는 경우
  • 캐시 저장소 조회

  • DB SELECT 조회 후 캐시 저장소에 저장

    - em.find(Memo.class, 1); 호출 시 캐시 저장소를 확인 후 값이 없으면, DB에 SELECT 조회 후 해당 값을 캐시 저장소에 저장하고 반환한다.

  • DB에서 데이터를 조회만 하는 경우에는 데이터의 변경이 발생하는 것이 아니기 때문에 트랜잭션이 없어도 조회가 가능합니다.

  • 사용 예시

@Test
@DisplayName("Entity 조회 : 캐시 저장소에 해당하는 Id가 존재하지 않은 경우")
void test2() {
    try {

        Memo memo = em.find(Memo.class, 1);
        System.out.println("memo.getId() = " + memo.getId());
        System.out.println("memo.getUsername() = " + memo.getUsername());
        System.out.println("memo.getContents() = " + memo.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

4. 쓰기 지연 저장소

  • JPA는 트랜잭션을 구현하기 위해 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션 commit 후 한번에 DB에 반영합니다.
  • Debuggig을 통한 확인
@Test
@DisplayName("쓰기 지연 저장소 확인")
void test6() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {
        Memo memo = new Memo();
        memo.setId(2L);
        memo.setUsername("Robbert");
        memo.setContents("쓰기 지연 저장소");
        em.persist(memo);

        Memo memo2 = new Memo();
        memo2.setId(3L);
        memo2.setUsername("Bob");
        memo2.setContents("과연 저장을 잘 하고 있을까?");
        em.persist(memo2);

        System.out.println("트랜잭션 commit 전");
        et.commit();
        System.out.println("트랜잭션 commit 후");

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}
  • commit 전을 확인해 보면 em > actionQueue를 확인해보면 insertions > executables에 Insert할 memo#2, memo#3 Entity 객체 2개가 들어가 있는 것을 확인할 수 있습니다.

  • actionQueue에 있던 insertions 데이터가 사라진 것을 확인할 수 있습니다.

5. Flush()

  • 트랜잭션이 commit 후 Sql들을 한번에 요청하기 이전에 flush()의 메서드 호출이 먼저 일어난다.
  • flush 메서드는 영속성 컨텍스트의 변경 내용들을 DB에 반영하는 역할을 수행한다.
  • 즉, flush 메서드를 commit 이전에 호출하게 되면, 바로 DB에 쓰지 지연 저장소의 SQL이 요쳥된다. 그 이후의 commit 메서드 호출에는 SQL 기록이 없다.

Insert, Update, Delete 즉, 데이터 변경 SQL을 DB에 요청 및 반영하기 위해서는 트랜잭션이 필요합니다.

6. Dirty Checking

  • em.update(entity); 같은 메서드를 지원하지 않는다.

  • JPA에서는 영속성 컨텍스트에 entity가 저장이 될때, 최초의 상태를 저장한다.

  • 트랜잭션이 commit되고 em.flush(); 가 호출되면 Entity의 현재 상태와 저장한 최초 상태를 비교 후, 변경 내용이 있다면 update SQL을 생성하여 쓰기 지연 저장소에 저장하고 모든 쓰기지연 저장소의 SQL을 DB에 요청한다.

  • 마지막으로 DB 트랜잭션이 Commit되면서 반영된다.

  • 즉, 자동으로 변화를 감지하고 updateSQL이 생성되고 반영된다. --> Dirty Checking이라고 부른다

  • 사용 예시

@Test
@DisplayName("변경 감지 확인")
void test8() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {
        System.out.println("변경할 데이터를 조회합니다.");
        Memo memo = em.find(Memo.class, 4);
        System.out.println("memo.getId() = " + memo.getId());
        System.out.println("memo.getUsername() = " + memo.getUsername());
        System.out.println("memo.getContents() = " + memo.getContents());

        System.out.println("\n수정을 진행합니다.");
        memo.setUsername("Update");
        memo.setContents("변경 감지 확인");

        System.out.println("트랜잭션 commit 전");
        et.commit();
        System.out.println("트랜잭션 commit 후");

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}
  • 디버깅을 통한 확인

    - entityEntry > loadedState는 조회했을 때 즉, 해당 Entity의 최초 상태
    - commit이후 flush가 호출되면 비교를 한 후, 변경이 있다면 update Sql 생성 및 요청

5. Entity의 상태

1. 비영속

  • new 연산자를 통해 인스턴스화 된 Entity 객체를 의미합니다.
  • 영속성 컨텍스트에 저장되기 이전의 상태

2. 영속

em.persist(memo);
  • persist(entity) : 비영속 Entity를 EntityManager를 통해 영속성 컨텍스트에 저장하여 관리되고 있는 상태로 만듭니다.
  • 디버깅을 통한 확인

3. 준영속

  • 영속성 컨텍스트에 저장되어 관리되다가 분리된 상태를 의미
  1. detatch(entity) : 특정 Entity만 준영속 상태로 전환
  • 준영속 상태로 전환되면 1차 캐시 즉, 캐시 저장소에서 제거되기 때문에 JPA의 관리를 받지 못해 영속성 컨텍스트의 어떠한 기능도 사용할 수 없다.
  • 디버깅을 통한 확인
  1. clear() : 영속성 컨텍스트를 완전히 초기화
  • 영속성 컨텍스트의 모든 Entity를 준영속 상태로 전환합니다.
  • 영속성 컨텍스트 틀은 유지한 채, 알맹이만 없는 상태가 되기 때문에, 다시 find() 호출과 같이 영속성 컨텍스트 이용은 가능하다.
  • 디버깅을 통한 확인
  • 다시 memo#1 Entity를 조회하여 영속성 컨텍스트에 저장된 것을 확인할 수 있다.
  1. close() : 영속성 컨텍스트를 종료
  • 컨텍스트가 관리하던 영속성 상태의 Entity들은 모두 준영속 상태로 변경
  • clear()와 다르게 아예 종료가 되기 때문에 계속해서 영속성 컨텍스트를 사용할 수 없다.
  • 디버깅을 통한 확인
    - em.close(); 메서드 호출 이후 EntityManager를 사용하려고 하자 오류가 발생
  1. merge(entity)
  • 준영속 상태에서 다시 영속 상태로 바꾸는 방법
  • 전달받은 Entity를 사용하여 새로운 영속 상태의 Entity를 반환한다.
  • 비영속, 준영속 모두 파라미터로 받을 수 있으며 상황에 따라 ‘저장’을 할 수도 ‘수정’을 할 수도 있습니다.
  • 작동 방식
    - 파라미터로 전달된 Entity의 식별자 값으로 영속성 컨텍스트 조회
    1. 영속성 컨텍스트에 없다면?(준영속 상태)
    • DB에서 새롭게 조회 이후, 영속성 컨텍스트에 저장
    • 전달받은 Entity값과 병합
    • Update Sql이 수행(수정)
    1. DB에도 없다면?
    • 새롭게 생성한 Entity를 영속성 컨텍스트에 저장
    • Insert Sql이 수행(저장)

4. remove()

 em.remove(memo);
  • remove(entity) : 삭제하기 위해 조회해온 영속 상태의 Entity를 파라미터로 전달받아 삭제 상태로 전환합니다.

출처

내일배움캠프 Spring Master강의

profile
밤낮없는개발자

0개의 댓글