상세 컨텐츠

본문 제목

JPA 엔티티 CRUD(조회 등록 수정 삭제)

기록 - 프로그래밍/Java

by wjjun 2024. 1. 1. 00:10

본문

 

엔티티 조회

영속성 컨텍스트는 내부에 캐시를 가지고 있습니다. 이것이 1차 캐시입니다.

컨텍스트 내부에 Map이 있고 Key 값은 @Id로 매핑한 식별자이며, 값은 엔티티 인스턴스 입니다.

1차 캐시의 키는 식별자 값입니다. 1차 캐시에 데이터가 없다면 데이터 베이스를 조회합니다.

Member member = new Member(); // entity
member.setId("member1"); // @Id

 

find() 메서드에 첫 번째 파라미터는 엔티티 클래스 타입이며, 두 번째 파라미터가 엔티티 식별자 값입니다.

Member member = entityManager(Member.class, "member1");
public <T> T find(Class<T> entityClass, Object primaryKey);

 

영속성 컨텍스트는 동일성과 성능의 이점이 있습니다.

영속성 컨텍스트에서 1차 캐시 또는 데이터베이스에서 1회 조회하면 member1, member2 모두 엔티티 인스턴스를 얻을 수 있습니다. 이때 member1, 2는 동일한 엔티티 주소값이 담긴 인스턴스를 반환하기 때문에 동일성이 보장됩니다. 

Member member1 = entityManager(Member.class, "member1");
Member member2 = entityManager(Member.class, "member2");

 

 

엔티티 등록

데이터를 변경할 때 트랜잭션이 커밋되기 전까지 데이터베이스에 SQL문이 반영되지 않는 "쓰기지연" 개념을 확인할 수 있습니다.

EntityManager em = entityManagerFactory.createEntityManager();
EntityTransaction transaction = em.getTransaction();

// 엔티티 매니저가 데이터 변경을 위해 트랜잭션을 시작합니다
transaction.begin();

// 아직 INSERT SQL을 데이터베이스에 전달하지 않습니다.
em.persist(member1);
em.persist(member2);

// 트랜잭션 커밋을 실행하면서 데이터베이스 앞선 INSERT SQL이 전달됩니다.
transaction.commit();

위 코드에서 트랜잭션이 커밋되기 전까지 INSERT SQL은 내부 쿼리 저장소에 쌓게 됩니다. 커밋이 실행되는 시점에 데이터베이스에 보내는 쓰기 지연(transactional write-behind)이 지원됩니다.

영속성 컨텍스트 내부에는 Map 자료구조로 된 1차 캐시와 쓰기지연 목적의 SQL 저장소가 있다고 생각하면 됩니다.

트랜잭션을 커밋하면 엔티티 매니저는 영속성 컨텍스트를 flush 합니다. 이 과정이 변경사항을 데이터베이스에 동기화하는 작업입니다.

 

쓰기 지연 SQL 저장소에 있는 쿼리는 하나의 트랜잭션 안에 커밋 기준에 따라 성공, 실패의 결과가 동일하게 나옵니다.

A, B, C SQL문이 저장소에 있을 때 트랜잭션이 실패하면 전부 rollback 되며, 성공시 A, B, C SQL문이 전부 commit 되는 것을 의미합니다.

 

 

엔티티 수정

Member member1 = em.find(Member.class, "member1");

member1.setName("mem1");
member1.setName("mem2");

// em.update()

엔티티 데이터를 변경합니다. 첫 번째 setName을 하고 update()를 해야 할 것 같지만 실행을 하지 않아도 변경사항 1, 2번이 자동으로 반영됩니다. 이것이 변경 감지이며 변경 감지는 영속성 컨텍스트가 관리하는 영속 상태의 엔티티에만 적용됩니다. JPA는 엔티티를 영속성 컨텍스트에 보관할 때 최초 상태를 복사해서 스냅샷을 저장합니다. 그리고 플러시 시점에 스냅샷과 엔티티를 비교해 변경된 엔티티를 찾습니다.

 

1차 캐시 자료구조 - 식별자키 / 엔티티 / 스냅샷

 

JPA Update 기본 전략은 엔티티의 모든 필드를 업데이트 합니다.

그 이유는 모든 필드를 사용하면 수정 쿼리가 항상 동일합니다. 그렇기 때문에 재사용이 가능합니다. 데이터베이스에 동일 쿼리를 보내면 데이터베이스는 이전에 한번 사용된 쿼리를 재사용할 수 있습니다.

 

모든 필드를 업데이트 하기를 원하지 않을 시 DynamicUpdate 어노테이션을 사용해 수정된 데이터만 동적으로 Update SQL을 생성합니다. 

 

 

엔티티 삭제

엔티티 삭제는 삭제 대상 엔티티를 조회하고 삭제합니다.

Member member = em.find(Member.class, member1);
em.remove(member);

엔티티 삭제도 쓰기 지연 SQL 저장소에 등록되며 트랜잭션 커밋이 이뤄지면 데이터 베이스에 저장소에 있는 SQL이 전달됩니다. 삭제된 엔티티는 더 이상 사용하지 않고 GC 처리되도록 두는 것이 좋습니다.

 

 

플러시

플러시는 영속성 컨텍스트 변경 내용을 데이터베이스에 반영합니다.

플러시를 실행하면 변경 감지가 동작하여 영속성 컨텍스트와 모든 엔티티를 스냅샷과 비교하여 수정된 엔티티를 조회합니다. 수정된 엔티티는 수정 쿼리를 만들어 쓰기 지연 SQL 저장소에 등록합니다. 그 다음 쓰기 지연 SQL 저장소의 쿼리를 데이버테이스 전송합니다.

JPA는 기본적으로 트랜잭션 커밋이 시점에 플러시가 자동으로 호출됩니다. (JPQL 포함)

 

플러시 방법 3가지

em.flush() 시 동작

트랜잭션 커밋 시 동작

JPQL 쿼리 실행 시 동작

 

 

관련글 더보기

댓글 영역