-
[JPA] JPA 특징 정리JPA 2020. 3. 24. 04:47반응형
JPA(Java Persistence API)란?
자바 진영의 ORM 기술 표준. JPA는 인터페이스이며 이것의 구현체는 Hibernate, EclipseLink, DataNucleus가 존재한다.
ORM(Object-relational mapping)이란?
객체와 관계형 데이터베이스를 중간에서 매핑해주는 기술. 객체를 Collection에 저장하고 조회하는 것처럼 DB 테이블에서도 저장하고 조회할 수 있다.
EBJ - 엔티티 빈이라는 기술이 ORM의 시초지만 성능과 사용방법이 너무 뒤떨어져 사용도가 낮았고 ORM 기술을 이용한 hibernate가 오픈소스로 등장하게 된다. 많은 인기를 얻은 hibernate는 자바 진영에서 JPA로 재정의 되어 표준 스펙으로 현재까지 널리 사용되고 있다.
JPA를 사용하는 이유?
기존의 SQL 중심의 개발에서 객체 중심의 개발을 가능하게 하며, 높은 생산성과 SQL의 의존성이 낮아져 유지보수에도 좋다.
JPA의 특징
1차캐시
애플리케이션이 실행되는 순간 EntityManagerFactory가 단 한 개 생성되고 애플리케이션 전체에서 공유되는 특성을 가지고 있다. 애플리케이션 전체에서 공유되는 EntityManagerFactory는 트랜잭션 단위당 한 개씩 EntityManager라는 것을 추가 생성하고 트랜잭션이 끝날 때 같이 소멸시킨다. 각각의 EntityManager는 영속성 컨텍스트라는 논리적 공간을 가지고 있는데 그 공간 속에 1차캐시가 존재한다.
1차캐시는 Id와 엔티티를 Map의 형태로 저장해 두고 있는 공간이며 모든 데이터 조작에 앞서 1차캐시의 공간을 먼저 이용한다. 이 공간은 트랜잭션과 생명주기를 함께한다.
// id 값 : 1 // 첫번째 데이터 호출 // 1. 영속성 컨텍스트에 id 값이 1인 Member 엔티티가 존재하는지 확인 // 2. 존재하지 않으므로 DB select 문을 날려 영속성 컨텍스트에 저장하고 반환 Member member = em.find(Member.class, 1); // 같은 데이터 호출 // 1. 영속성 컨텍스트에 id 값이 1인 Member 엔티티가 존재하는지 확인 // 2. 존재하므로 DB에 접근하지 않고 영속성 컨텍스트에서 바로 반환 Member member = em.find(Member.class, 1);
DB에 select SQL을 날리는 것이 아닌 영속성 컨텍스트 내부의 1차캐시에 해당하는 Id값을 지닌 엔티티가 존재하는 것을 우선적으로 확인한다.
1차캐시 내부에서 엔티티를 가져오기 때문에 객체 동일성을 띄고 있다. 영속성 컨텍스트 1차캐시에 존재하는 엔티티를 두 번 불러와 == 비교를 통해 true를 반환받을 수 있다.
쓰기지연
트랜잭션을 커밋할 때까지 C, U. D(생성, 수정, 삭제)를 영속성 컨텍스트 내부의 쓰기지연 SQL 저장소에 저장해둔다.
// 트랜잭션 시작 EntityTransaction.begin(); Member member = new Member(); member.setId(1); member.setName("devyu"); // persist()에서 바로 SQL을 날리지 않는다! entityManager.persist(member); // 트랜잭션 commit, SQL 날리는 순간. EntityTransaction.commit();
persist() 메서드를 통해 DB에 저장하지만 실제 저장되는 시점은 persist(member); 라인이 아닌 commit() 라인이 된다. 영속성 컨텍스트 내부에는 1차캐시 뿐만 아니라 쓰기지연 SQL 저장소가 존재한다. SQL 저장소는 commit() 직전까지의 모든 데이터 조작 SQL을 저장해 두고 한 번에 쿼리를 생성해서 DB에게 보내는 역할을 한다.
지연로딩과 즉시로딩
지연로딩은 객체가 실제 사용될 때 DB에 SQL를 날려 데이터를 가져오는 방식이고 즉시로딩은 처음부터 연관 객체를 모두 Join으로 엮어 데이터를 가져오는 방식을 의미한다.
// 지연로딩 설정일 경우 Member member = memberDao.find(memberId); // select * from Member... Team team = member.getTeam(); // select * from Team... String teamNo = team.getNo(); // 즉시로딩 설정일 경우 Member member = memberDao.find(memberId); // select * from Member join Team ... Team team = member.getTeam(); String teamNo = team.getNo();
지연로딩의 경우 각각 필요할 때 member와 team 두 번의 select SQL 수행한다.
즉시로딩의 경우 처음부터 member와 team을 조인해서 한 번에 가져온다.
변경감지(dirty-checking)
JPA에서는 find() 조회, persist() 생성, remove() 제거 메서드가 존재하는데 일반적인 update() 같은 수정을 위한 메서드가 없다. 잠시 생각해보면 자바 컬렉션에서는 List를 get() 메서드로 가져온 뒤 값을 수정하고 다시 add()로 넣어주는 과정이 필요한가? ORM 기술이 보다 객체지향적으로 RDB를 사용하도록 설계되어있기 때문에 update를 위한 메서드는 따로 필요하지 않기 때문이다.
그렇다면 예시를 보자.
// id 값이 1인 Member 엔티티를 조회 // member.name = "devyu" Member member = em.find(Member.class, 1); member.setName("puregyu");
간단하게 값을 변경해주면 수정 끝이다. 해당 트랜잭션이 commit 될 때 자동으로 변경된 부분 감지해서 "devyu"가 "puregyu"로 변경된 update SQL 쿼리를 날린다. 좀 더 자세히 들어가면 find() 메서드를 통해 데이터를 조회하면 영속성 컨텍스트 내부의 1차캐시는 스냅샷이라고 불리는 공간에 엔티티를 복제해둔다. 그 뒤 로직을 통해 Member 이름을 변경하고 commit이 되면 변경된 Member 엔티티와 스냅샷에 저장된 원본 Member 엔티티가 다를 경우 update SQL을 DB에 날리게 된다.
반응형'JPA' 카테고리의 다른 글
[JPA] 페치조인(fetch join)이란? (0) 2020.04.03 [JPA] 경로표현식(묵시적 내부조인과 명시적 내부조인) (0) 2020.04.03 [JPA] JPA 쿼리 지원 방식(JPQL) (0) 2020.03.30 [JPA] 데이터 타입 분류 (Embedded) (0) 2020.03.29 [JPA] 상속관계 매핑 전략(); (1) 2020.03.27