엔티티(Entity 객체)
엔티티는 데이터베이스의테이블과 매핑되는 클래스입니다.
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
// Getter, Setter, Constructor, Methods...
}
User 클래스에 `@Entity` 어노테이션을 사용하여 엔티티로 지정해줍니다. 그럼 해당 엔티티는 데이터베이스의 User 테이블과 매핑되며, 객체의 속성과 테이블의 컬럼 간의 매핑이 이루어집니다. JPA 엔티티 매핑 속성 자세히 보기
엔티티 매니저(JPA EntityManager)
JPA에서 EntityManager는 엔티티 객체를 관리하여 어플리케이션과 데이터베이스 간의 중간 매개체로 작용합니다.
- 영속 상태관리:
엔티티 매니저를 사용하여 객체를 영속 상태로 만들 수 있습니다. 영속 상태인 객체는 데이터베이스의 변경을 자동으로 감지하고 반영합니다. - 트랜잭션 관리:
엔티티 매니저는 트랜잭션을 시작하고 커밋 또는 롤백하는 기능을 제공합니다. - 쿼리 실행:
엔티티 매니저는JPQL(Java Persistence Query Language)을 사용하여 데이터베이스에서 데이터를 검색하는 쿼리 작업을 수행할 수 있습니다.
EntityManager가 제공하는 API
일단은 아래와 같은 것들이 있다만 알아두고 개념을 학습한 후에 필요하면 API를 보고 사용하자!!
void persist(java.lang.Object entity) SQL의 INSERT문에 해당.
persist 메소드를 실행한 시점에는 데이터베이스의 SQL은 실행되지 않고 영속성 컨텍스트에 축적된다.<T> find(java.lang.Class<T> entityClass,
java.lang.Object primaryKey)기본키를 지정해서 Entity를 검색 및 반환.
영속성 컨텍스트에 해당하는 Entity가 존재하지 않는 경우 DB에 SQL(SELECT 문)을 발행해 해당 데이터를 취득하고 Entity를 생성해서 반환.<T> T merge(T entity) 영속성 컨텍스트에서 관리되고 있지만 분리 상태가 된 Entity를 영속성 컨텍스트에서 다시 관리한다. void remove(java.lang.Object entity) Entity를 영속성 컨텍스트 및 DB에서 삭제.
persist나 merge와 마찬가지로 DB에 SQL(DELETE문)이 바로 발행되지 않고 영속성 컨텍스트에 축적된다.void flush() 영속성 컨텍스트에 축적된 모든 Entity의 변경 정보를 DB에 강제적으로 동기화.
일반적으로 DB에 반영하는 작업 commit할 때이지만 commit 이전에 반영할 필요가 있는 경우 사용.void refresh(java.lang.Object entity) Entity의 상태를 DB의 데이터로 강제 변환. <T> TypeQuery<T> createQuery(
java.lang.String query,
java.lang.Class<T> resultClass)기본 키 이외의 것으로 DB에 접근하는 경우에 JPA용 쿼리를 실행해 Entity를 취득하거나 변경할 수 있다. void detach(java.lang.Object entity) Entity를 영속성 컨텍스트에서 삭제하고 분리 상태로 만든다. void clear() 영속성 컨텍스트에서 관리되는 모든 Entity를 분리 상태로 만든다. boolean contains(java.lang.Object entity) Entity가 영속성 컨텍스트에서 관리되는지를 반환한다.
엔티티 매니저 팩토리와 엔티티 매니저
`엔티티 매니저(entity manager)`를 사용하기 위해선 생성부터 해주어야 합니다. `엔티티 매니저 팩토리`가 `엔티티 매니저`를 만들어줍니다. 이때 엔티티 매니저 팩토리는 어플리케이션 전체에서 한 번만 생성하고 공유해서 사용되어야 합니다.
엔티티 매니저 팩토리는 데이터 변경 요청이 발생하면 트랜잭션 단위마다 엔티티 매니저를 생성합니다. 트랜잭션 안에서 변경이 이루어지고, 변경이 완료되면 엔티티 매니저를 삭제합니다.
엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하기 때문에 서로 다른 스레드 간에 공유해도 됩니다.
하지만 엔티티 매니저는 여러 스레드가 동시에 접근하면 동시성 문제가 발생하기 때문에 스레드 간에 절대 공유하면 안됩니다.
// 엔티티 매니저 팩토리 생성
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa");
// 엔티티 매니저 생성
EntityManger em = emf.createEntityManager();
영속성 컨텍스트: Persistence Context
엔티티 매니저는 엔티티 객체를 관리할 때 영속성 컨텍스트에 저장하여 관리합니다. 영속성 컨텍스트는 어플리케이션과 데이터베이스 사이에서 객체를 보관하는 가상의(논리적인) 공간입니다. 영속성 컨텍스트에 넣어놓음으로 데이터베이스에서 효과적으로 데이터를 가져올 수 있고, 엔티티를 사용할 수 있습니다. 즉, 영속성 컨텍스트가 데이터베이스의 캐시와 같은 역할을 하고 있으며, DB에 데이터를 저장한다는 표현이 아닌 영속성 컨텍스트를 통해 엔티티를 영속화 한다는 표현이 정확합니다.
영속성 컨텍스트는 엔티티를 식별자 값(@Id)으로 구분합니다. 영속성 컨텍스트는 엔티티를 데이터베이스에 반영하는데 이를 플러시(flush)라 합니다.
플러시(flush)란?
플러시는 영속성 컨텍스트의 (변경)내용을 데이터베이스에 모두 반영합니다.
기본적으로 쿼리들 간 순서가 보장되어 있기 때문에 flush가 호출되면 차례대로 쿼리가 수행됩니다. 삽입, 수정, 삭제 쿼리의 경우 쿼리가 순차적으로 수행되기 때문에 문제가 생기지 않지만, 조회 쿼리 경우 데이터 변경 사항이 먼저 적용되지 않으면 데이터 불일치가 발생할 수 있습니다. 때문에 JPQL 쿼리 실행 시 그 전에 엔티티 매니저는 flush를 호출한 후 JPQL를 실행합니다.
영속성 컨텍스트를 플러시하는 방법 3가지:
1. em.flush() 직접 호출
2. 트랜잭션 커밋 시 플러시 자동 호출
3. JPQL 쿼리 실행 시 플러시 자동 호출
영속성 컨텍스트 특징
영속성 컨텍스트들의 특징들이 갖는 공통점은 모두 데이터베이스의 접근을 최소화해 성능을 높인다는 것입니다.
1차 캐시
영속성 컨텍스트는 내부에 엔티티를 저장하는 캐시를 가지고 있는데 이것을 1차 캐시라 합니다. 1차 캐시의 키는 엔티티의 @Id 어테이션이 달린 기본키 역할을 하는 식별자이며 값은 엔티티입니다. 엔티티를 조회하면 1차 캐시에서 데이터를 조회하고 값이 있으면 반환합니다. 값이 없으면 데이터베이스에서 조회해 1차 캐시에 저장한 다음 반환합니다. 이를 통해 캐시된 데이터를 조회할 때에는 데이터베이스를 거치치 않아도 되어 중복된 로드를 방지하고 성능을 향상시킬 수 있습니다.
동일성 보장
영속성 컨텍스트는 같은 엔티티 ID를 가진 객체를 1차 캐시에서 찾아 동일한 객체로 반환합니다. 이를 통해 동일한 ID를 가지는 엔티티가 여러 번 조회되더라도 같은 객체 인스턴스를 반환하여 동일성을 보장합니다.
트랜잭션을 지원하는 쓰기 지연
쓰기 지연(transactional write-behind)은 트랜잭션을 커밋하기 전까지는 데이터베이스에 엔티티를 저장하지 않고 내부(쓰 지연 저장소)에 쿼리를 모았다가 트랜잭션을 커밋하면 모았던 쿼리를 한번에 실행하는 것을 의미합니다. 예를 들어, 데이터 추가 쿼리가 5개라면 영속성 컨텍스트는 트랜잭션을 커밋하는 시점에 5개의 쿼리를 한꺼번에 쿼리를 전송합니다. 이를 통해 적당한 묶음으로 쿼리를 요청할 수 있어 데이터베이스 시스템의 부담을 줄일 수 있습니다.
변경 감지
영속성 컨텍스트는 1차 캐시에 저장되어 있는 엔티티의 값과 현재 엔티티의 값을 비교해서 변경된 값이 있다면 변경 사항을 감지해 변경된 값을 데이터베이스에 자동으로 반영합니다. 트랜잭션 커밋 시 변경된 엔티티만 적용하여 데이터베이스와 동기화합니다. 이를 통해 쓰기 지연과 마찬가지로 적당한 묶음으로 쿼리를 요청할 수 있고, 데이터베이스 시스템의 부담을 줄일 수 있습니다.
지연 로딩
지연 로딩은 엔티티의 연관 관계를 필요한 시점에 로드하는 기능으로, 필요한 데이터만 로드하여 성능을 최적화할 수 있습니다.
엔티티 생명주기
엔티티는 4가지의 상태를 가집니다.
- 비영속(new/transient): 영속성 컨텍스트와 전혀 관계가 없는 상태
- 영속(managed): 영속성 컨텍스트가 관리하는 상태 (관리 상태)
- 준영속(detached): 영속성 컨텍스트가 관리하지 않는 상태 (분리 상태)
- 삭제(removed): 삭제된 상태
// 엔티티 매니저가 엔티티를 관리하지 않는 상태(비영속 상태)
Member member = new Member(1L, "kim");
// 엔티티가 관리 상태가 됩니다. (관리 상태)
em.persist(member);
// 엔티티 객체가 분리된 상태가 됩니다. (분리 상태)
em.detach(member);
// 엔티티 객체가 삭제된 상태가 됩니다. (삭제 상태)
em.remove(member);
준영속(분리) 상태 만드는 여러 방법
엔티티를 준영속 상태로 만들기 위한 방법으로 다음과 같은 방법들이 있습니다.
- em.detach(): 특정 엔티티만 준영속 상태로 만듭니다.
- em.close(): 영속성 컨텍스트를 닫습니다.
- em.clear(): 영속성 컨텍스트의 데이터를 비웁니다.
준영속(분리) 상태 엔티티
준영속(분리) 상태 엔티티는 거의 비영속 상태에 가깝습니다. 비영속 상태와 마찬가지기 때문에 이 상태에서는 관리 상태로 되돌릴 수단이 있습니다. 해당 엔티티는 더는 영속성 컨텍스트의 관리를 받지 못하기 때문에 영속성 컨텍스트가 제공하는 1차 캐시, 동일성 보장, 쓰기 지연, 변경 감지, 지연 로딩 같은 기능들을 사용할 수 없습니다.
'Spring Data > JPA' 카테고리의 다른 글
JPA 생성 및 수정 날짜 자동 처리를 위한 공통 엔티티 만들기 @MappedSuperclass, @EnableJpaAuditing (1) | 2023.08.17 |
---|---|
JPA ddl-auto: 데이터베이스 스키마 자동 생성 전략 (0) | 2023.08.17 |
JPA 엔티티 매핑: 객체와 데이터베이스 테이블의 매핑 (0) | 2023.08.17 |
JPA: 자바 ORM 표준 (0) | 2023.08.17 |
JPA N+1 문제 (0) | 2023.01.13 |