1. ORM (Object-Relational Mapping)
ORM은 객체 지향 언어에서 관계형 데이터베이스의 데이터를 다루기 위한 기술로, 객체와 관계형 데이터베이스의 테이블간의 매핑을 자동화해주는 방식이다.
데이터베이스의 데이터를 객체 형태로 변환하고, 객체를 데이터베이스에 저장할 수 있도록 함으로써 개발자가 SQL을 직접 작성하지 않게 해준다.
특징
- 객체와 테이블의 매핑: 클래스와 데이터베이스 테이블을 1:1로 매핑하여 객체로 데이터를 다루게 한다.
- SQL 자동 생성: 데이터를 저장하거나 조회할 때 ORM 프레임워크가 SQL을 자동으로 생성하고 실행한다.
- 데이터베이스 추상화: 특정 데이터베이스에 종속되지 않고, 추상화 계층을 통해 여러 데이터베이스 시스템을 쉽게 변경할 수 있다.
2. JPA (Java Persistence API)
JPA는 자바진영에서 ORM을 구현하기 위한 표준 API이다. JPA 자체는 인터페이스이며, 이를 직접 구현하지 않기 때문에 Hibernate와 같은 ORM 프레임워크가 JPA를 구현한다.
개념
- 엔티티(Entity): 데이터베이스의 테이블에 매핑되는 자바 클래스.
- 엔티티 매니저(EntityManager): 엔티티의 상태를 관리하고 영속성 컨텍스트를 조작하며, CRUD 작업을 수행.
- 엔티티 매니저 팩토리(EntityManagerFactory): EntityManager를 생성하는 팩토리 객체로, 애플리케이션 전체에서 하나만 생성하여 사용.
- 영속성 컨텍스트(Persistence Context): EntityManager가 관리하는 엔티티 객체의 집합으로, 엔티티의 상태 변경을 추적.
- 쿼리 언어(JPQL): 객체 지향 쿼리 언어로, 엔티티 객체를 대상으로 쿼리를 수행.
EntityManagerFactory
- 애플리케이션이 시작될때 EntityMangerFactory를 생성한다. 이 과정에서 persistence.xml파일을 로드하고 설정 정보를 읽는다.
- 애플리케이션의 각 트랜잭션이나 작업단위에서 EntityMangerFactory를 통해 EntityManager를 생성한다. 이때 EntityManger는 쓰레드 공유를 하지 않는다. (동시성 이슈 발생 가능)
- EntityManager는 영속성 컨텍스트를 관리하며 엔티티의 상태 변화를 추적, 데이터베이스 CRUD작업 및 JPQL 쿼리를 실행
3. 영속성 컨텍스트
영속성 컨텍스트(Persistence Context)란? 엔티티(Entity) 객체를 관리하는 일종의 1차 캐시로서, 데이터베이스와 애플리케이션 사이의 중간 계층 역할을 한다. EntityManager에 의해 관리되는 객체들의 집합이며 EntityManager를 통해 영속성 컨텍스트에 접근 한다.
엔티티의 생명주기
- 비영속(Transient): 영속성 컨텍스트와 전혀 관계없는 새로운 상태.
- 영속(Managed): 영속성 컨텍스트에 의해 관리되는 상태.
- 준영속(Detached): 영속성 컨텍스트에서 분리된 상태.
- 삭제(Removed): 삭제되기로 표시된 상태
영속성 컨텍스트의 이점
1. 1차 캐시
//엔티티를 생성한 상태(비영속)
Member member = new Member();
member.setId("member1");
member.setUsername("회원1");
//엔티티를 영속
em.persist(member);
//1차 캐시에서 조회
Member findMember1 = em.find(Member.class, "member1");
Member findMember2 = em.find(Member.class, "member1");
System.out.println(findMember1 == findMember2); // 동일성 비교 true
JPA는 먼저 1차 캐시에서 조회한다. 같은 영속성 컨텍스트 내에서는 동일한 식별자를 가진 엔티티는 동일한 인스턴스로 관리된다.
2. 쓰기 지연
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
//엔티티 매니저는 데이터 변경시 트랜잭션을 시작해야 한다.
transaction.begin(); // [트랜잭션] 시작
em.persist(memberA);
em.persist(memberB);
//여기까지 INSERT SQL을 데이터베이스에 보내지 않는다.
//커밋하는 순간 데이터베이스에 INSERT SQL을 보낸다.
transaction.commit(); // 커밋
여런 변경사항을 모아서 한 번에 데이터베이스에 반영하여 성능을 최적화한다. 트랜잭션이 커밋될 때 까지 변경 사항을 보류하여 일관성을 유지한다.
3. 변경 감지
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin(); // [트랜잭션] 시작
// 영속 엔티티 조회
Member memberA = em.find(Member.class, "memberA");
// 영속 엔티티 데이터 수정
memberA.setUsername("hi");
memberA.setAge(10);
//em.update(member) 이런 코드가 있어야 없어도 UPDATE 쿼리 실행
transaction.commit(); // 커밋
엔티티의 필드 값이 변경되면 트랜잭션 커밋 시점에 자동으로 데이터베이스에 반영한다. 개발자가 명시적으로 update쿼리를 작성하지 않아도 된다.
플러시
플러시(Flush)란, 영속성 컨텍스트의 상태를 데이터베이스와 동기화(반영)하는 과정이다. 영속성 컨텍스트의 변경 감지 후 수정된 엔티티를 쓰기지연 SQL에 등록 후 쓰기 지연 SQL 저장소의 쿼리를 데이터베이스에 전송한다.
다만 주의사항으로는 플러시를 했다고 해서 영속성 컨텍스트를 비우지 않는다는 점이다. 영속성 컨텍스트를 비울려면 clear( ) 메서드를 사용하면 된다.
영속성 컨텍스트를 플러시 하는 방법
- em.flush( ) - 직접 호출
- 트랜잭션 커밋 - 플러시 자동 호출
- JPQL 쿼리 실행 - 플러시 자동 호출
JPQL 쿼리 실행시 플러시가 자동으로 호출되는 이유는 컨텍스트의 변경 사항을 데이터베이스와 동기화 하여 쿼리 결과의 일관성과 정확성을 보장하기 위해서이다. 코드를 통해 알아보자
em.persist(memberA);
em.persist(memberB);
em.persist(memberC);
//중간에 JPQL 실행
query = em.createQuery("select m from Member m", Member.class);
List<Member> members= query.getResultList();
memberA, memberB, memberC를 영속성 컨텍스트에 저장을 하고 중간에 DB에서 조회를 한다 했을 때 만약 Flush가 호출되지 않는다면 memberA, memberB, memberC를 제외한 결과값이 조회되기 때문이다.
이러한 이유 때문에 JPQL을 실행하면 자동으로 Flush를 해준다.
플러시 모드옵션
- FlushModeType.AUTO: 커밋이나 쿼리를 실행할 때 동작 (기본 값)
- FlushModeType.COMMIT (커밋할 때만 플러시)
'JPA' 카테고리의 다른 글
[JPA] JPA의 타입 (임베디드 타입, 값 타입, 컬렉션 타입)이란? (1) | 2024.12.20 |
---|---|
[JPA] 프록시와 지연로딩, 즉시로딩 (0) | 2024.11.26 |
[JPA] 연관관계 매핑 (0) | 2024.11.25 |
[JPA] 엔티티 매핑 (0) | 2024.11.25 |
[JPA] 영속성 전이 CASCADE, OrphanRemoval = true 이해 및 차이 (0) | 2024.11.19 |