이 글은 인프런 김영한님의 Spring 강의를 바탕으로 개인적인 정리를 위해 작성한 글입니다.
JPA 구동 방식
엔티티 매니저 팩토리 (EntityManagerFactory)
정의
- EntityManagerFactory는 JPA 애플리케이션에서 EntityManager 인스턴스를 생성하기 위한 팩토리이다.
특징
- 비용이 많이 드는 객체: 생성하는 데 많은 리소스를 사용하므로 애플리케이션 전체에서 한 번만 생성하고 공유하는 것이 일반적이다.
- 애플리케이션 전체에서 공유: 여러 스레드에서 동시에 사용될 수 있다.
- 생명 주기: 애플리케이션 시작 시 생성되고, 애플리케이션 종료 시 닫힌다.
엔티티 매니저 (EntityManager)
정의
- EntityManager는 엔티티의 생명 주기(Life Cycle)를 관리하고, 데이터베이스 작업을 수행하는 객체이다.
특징
- 데이터베이스 연결 관리: 데이터베이스와의 연결을 관리하고, 쿼리 실행, 트랜잭션 관리 등의 작업을 수행한다.
- 영속성 컨텍스트: 엔티티 인스턴스들을 관리하는 영속성 컨텍스트(Persistence Context)를 제공한다. 영속성 컨텍스트는 엔티티의 상태 변화를 추적하고 데이터베이스와 동기화한다.
- 스레드 안전하지 않음: EntityManager는 스레드에 안전하지 않으므로 각 트랜잭션 또는 요청마다 새로운 인스턴스를 생성하여 사용해야 한다.
- 생명 주기: 일반적으로 짧은 생명 주기를 가지며, 각 트랜잭션마다 생성되고 종료된다.
주요 메서드
- persist(Object entity): 엔티티를 영속성 컨텍스트에 저장한다.
- merge(Object entity): 준영속 상태의 엔티티를 영속성 컨텍스트로 병합한다.
- remove(Object entity): 엔티티를 영속성 컨텍스트에서 제거한다.
- find(Class<T> entityClass, Object primaryKey): 기본 키로 엔티티를 조회한다.
- createQuery(String qlString): JPQL 쿼리를 생성한다.
- getTransaction(): 현재 트랜잭션을 반환한다.
객체와 테이블을 생성하고 매핑하기
create table Member ( id bigint not null, name varchar(255), primary key (id) );
package hellojpa; import javax.persistence.Entity; import javax.persistence.Id; @Entity public class Member { @Id private Long id; private String name; //Getter, Setter … }
@Entity 어노테이션은 해당 클래스가 JPA 엔티티임을 명시한다.
-클래스 선언부에 @Entity 어노테이션을 추가하여 해당 클래스가 엔티티임을 정의한다.
-엔티티 클래스는 반드시 @Entity 어노테이션을 가져야 하며, public 또는 protected로 선언된 기본 생성자가 있어야 한다.
-@Entity가 붙은 클래스는 반드시 @Id로 표시된 기본 키 필드를 가져야 한다.
-클래스 이름이 기본적으로 데이터베이스 테이블 이름으로 사용되지만, @Table 어노테이션을 사용하여 테이블 이름을 명시적으로 지정할 수 있다.
@Id 어노테이션은 엔티티의 기본 키를 정의하는 데 사용된다.
-엔티티 클래스의 필드나 메서드에 @Id 어노테이션을 추가하여 해당 필드가 기본 키임을 정의한다.
-모든 JPA 엔티티는 하나 이상의 @Id 어노테이션을 가져야 하며, 이를 통해 기본 키를 지정해야 한다.
-기본 키의 값을 자동으로 생성하기 위해 @GeneratedValue 어노테이션과 함께 사용할 수 있다. @GeneratedValue는 기본 키 생성 전략을 지정하는 데 사용된다.
주의 사항
-엔티티 매니저 팩토리는 하나만 생성해서 애플리케이션 전체에서 공유해야 한다.
- 엔티티 매니저는 쓰레드간에 공유를 하면 안된다. (공유를 하게되면 서비스의 장애를 일으킬 수 있다.)
- JPA의 모든 데이터 변경은 트랜잭션 안에서 실행된다.
영속성 컨텍스트
-"엔티티를 영구 저장하는 환경" 이라는 뜻
-EntityManager.persist(entity);
-영속성 컨텍스트는 논리적인 개념이다. 엔티티 매니저를 통해서 영속성 컨텍스트에 접근할 수 있다.
스프링 프레임워크 같은 컨테이너 환경에서는 엔티티 매니저와 영속성 컨텍스트가 N:1 의 관계이다.
엔티티의 생명주기
비영속 (Transient)
정의: 비영속 상태는 엔티티가 아직 영속성 컨텍스트에 의해 관리되지 않는 상태를 말한다.
특징
- 데이터베이스와 전혀 관련이 없다.
- 엔티티 매니저에 의해 관리되지 않는다.
- 영속성 컨텍스트에 포함되지 않기 때문에 영속성 컨텍스트의 기능(변경 감지, 쓰기 지연 등)을 사용할 수 없다.
Member member = new Member();
member.setId(1L);
member.setName("Lee");
영속 (Persistent)
정의: 영속 상태는 엔티티가 영속성 컨텍스트에 의해 관리되는 상태를 말한다.
특징
- 영속성 컨텍스트의 1차 캐시에 저장된다.
- 트랜잭션을 커밋하거나 flush()를 호출하면 변경 사항이 데이터베이스에 반영된다.
- 변경 감지(dirty checking) 기능이 적용되어 엔티티의 변경 사항이 자동으로 반영된다.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Member member = new Member();
member.setId(1L);
member.setName("Lee");
em.persist(member); // 엔티티를 영속성 컨텍스트에 저장
em.getTransaction().commit();
준영속 (Detached)
정의: 준영속 상태는 엔티티가 한 번 영속 상태였으나 현재는 영속성 컨텍스트에 의해 더 이상 관리되지 않는 상태를 말한다.
특징
- 영속성 컨텍스트가 닫히거나, detach(), clear(), close() 메서드가 호출되면 엔티티가 준영속 상태로 전환된다.
- 영속성 컨텍스트의 기능(변경 감지, 쓰기 지연 등)을 사용할 수 없다.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Member member = new Member();
member.setId(1L);
member.setName("Lee");
em.persist(member); // 엔티티를 영속성 컨텍스트에 저장
em.detach(member); // 엔티티를 준영속 상태로 전환
em.getTransaction().commit();
- em.detach(entity) : 특정 엔티티만 준영속 상태로 전환
- em.clear() : 영속성 컨텍스트를 완전히 초기화
- em.close() : 영속성 컨텍스트를 종료
삭제 (Removed)
정의: 삭제 상태는 엔티티가 영속성 컨텍스트와 데이터베이스에서 삭제될 예정인 상태를 말한다.
특징
- remove() 메서드를 호출하면 엔티티가 삭제 상태로 전환된다.
- 트랜잭션을 커밋하면 데이터베이스에서 해당 엔티티가 삭제된다.
- 삭제된 엔티티는 더 이상 영속성 컨텍스트에서 관리되지 않는다.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Member member = em.find(Member.class, 1L);
em.remove(member); // 엔티티를 삭제 상태로 전환
em.getTransaction().commit(); // 트랜잭션을 커밋하여 데이터베이스에서 삭제
주요 메서드
- persist(Object entity): 엔티티를 영속성 컨텍스트에 저장한다.
- remove(Object entity): 엔티티를 영속성 컨텍스트에서 제거한다.
- find(Class<T> entityClass, Object primaryKey): 기본 키를 통해 엔티티를 조회한다.
- merge(Object entity): 준영속 상태의 엔티티를 영속성 컨텍스트로 병합한다.
- flush(): 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영한다.
- clear(): 영속성 컨텍스트를 초기화하여 모든 엔티티를 준영속 상태로 만든다.
영속성 컨텍스트의 이점
엔티티 조회, 1차 캐시
- 영속성 컨텍스트는 엔티티를 1차 캐시에 저장하여 관리한다.
- 같은 엔티티를 반복 조회할 때, 데이터베이스를 다시 조회하지 않고 1차 캐시에서 가져온다. 이를 통해 성능을 최적화할 수 있다.
- 만약 조회시 1차 캐시에 존재하지 않는다면, DB에서 데이터를 가져오고 자동으로 영속 컨텍스트에 저장한다.
영속 엔티티의 동일성 보장
- 영속성 컨텍스트는 같은 트랜잭션 내에서 같은 식별자를 가진 엔티티에 대해 동일한 객체를 반환한다. 이는 엔티티의 동일성을 보장하여 일관된 데이터를 제공한다.
쓰기 지연(Write-behind)
- 영속성 컨텍스트는 엔티티의 변경 내용을 즉시 데이터베이스에 반영하지 않고, 트랜잭션을 커밋하거나 flush를 호출할 때까지 지연시킨다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // [트랜잭션] 커밋
변경 감지(Dirty Checking)
- 영속성 컨텍스트는 엔티티의 상태 변화를 감지하고, 트랜잭션이 커밋될 때 변경된 내용을 자동으로 데이터베이스에 반영한다.
- 이는 엔티티를 수정하고 flush 또는 commit 시 자동으로 UPDATE 쿼리를 생성하여 데이터베이스에 적용한다.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
transaction.commit(); // [트랜잭션] 커밋
- 최초로 1차 캐시에 저장될때, 스냅샷을 미리 생성하고 나중에 현재 스냅샷과 최초 스냅샷을 비교하여 변경점이 있으면 UPDATE SQL을 생성한다.
플러시
- 변경 감지
- 수정된 엔티티 쓰기 지연 SQL 저장소에 등록
- 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송(등록, 수정, 삭제 쿼리)
- 플러시(Flush)는 JPA에서 영속성 컨텍스트(Persistence Context)에 있는 변경 사항을 데이터베이스에 동기화하는 작업이다.
특징
- 영속성 컨텍스트에서 관리되는 엔티티의 변경 사항(INSERT, UPDATE, DELETE)을 SQL 문으로 데이터베이스에 보낸다.
- 트랜잭션이 커밋되기 전에 자동으로 발생하지만, 명시적으로 flush() 메서드를 호출하여 중간에 실행할 수도 있다.
- 일시적 동기화: 플러시는 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영하지만 트랜잭션을 종료하지는 않는다. 즉, 트랜잭션은 여전히 열려 있다.
플러시의 동작 시점
- 트랜잭션 커밋: 트랜잭션을 커밋할 때 자동으로 플러시가 발생하여 변경 사항이 데이터베이스에 반영된다.
- JPQL 쿼리 실행: JPQL 쿼리를 실행할 때 플러시가 발생하여 변경 사항이 쿼리 결과에 반영되도록 한다.
- 명시적 호출: EntityManager의 flush() 메서드를 호출하여 명시적으로 플러시를 수행할 수 있다.
플러시 모드 옵션
em.setFlushMode(FlushModeType.COMMIT)
- FlushModeType.AUTO : 커밋이나 쿼리(JPQL)를 실행할 때 플러시 (기본값)
- FlushModeType.COMMIT : 커밋할 때만 플러시(JPQL을 작성할 때도 플러시 되지 않음)
주의 사항
- 플러시는 영속성 컨텍스트를 비우지 않는다.
- 영속성 컨텍스트의 변경 내용을 데이터베이스에 동기화한다.
'Java Category > JPA' 카테고리의 다른 글
[JPA] 프록시(Proxy) (0) | 2024.07.26 |
---|---|
[JPA] 상속관계 매핑 (0) | 2024.07.25 |
[JPA] 다양한 연관관계 매핑 (5) | 2024.07.24 |
[JPA] 연관관계 매핑(단방향, 양방향)을 통한 객체 그래프 탐색 (0) | 2024.07.17 |
[JPA] 엔티티 매핑(Entity Mapping) (1) | 2024.07.16 |