티스토리 뷰

JPA(Java Persistence API)란

The Java Persistence API (JPA) is a Java specification for accessing, persisting, and managing data between Java objects / classes and a relational database. JPA is now considered the standard industry approach for Object to Relational Mapping (ORM) in the Java Industry.
JPA(Java Persistence API)는 Java 객체/클래스와 관계형 데이터베이스 간의 데이터 액세스, 유지 및 관리를 위한 Java 스펙이다. 
JPA는 이제 Java 생태계에서 ORM(Object to Relational Mapping)에 대한 표준 접근 방식으로 간주된다.
 - wikibooks
By itself, JPA is not a tool or framework; rather, it defines a set of concepts that can be implemented by any tool or framework.
JPA 자체는 툴이나 프레임워크가 아니고, 모든 도구나 프레임워크로 구현할 수 있는 일련의 개념을 정의한다.
 - infoworld

JPA Java의 ORM 표준 스펙으로 ORM의 기능을 제공하지는 않습니다. 이름 그대로 API 즉 기술에 대한 명세, 인터페이스의 모음입니다. 이런 JPA를 구현하는 대표적인 구현체로는 HIbernate, EclipseLink, DataNucleus 가 3개가 있습니다.

 

JPA가 필요한 이유 (장점)

JPA는 ORM의 기능들을 추상화한 인터페이스 모음이기 때문에 JPA를 사용한다는 것은 ORM을 이용하는 것이라고 이야기할 수 있습니다. 따라서 ORM의 기능에 대해 이해한다면 ORM이 주는 이점들에 대해 알 수 있을 것입니다.  ORM에 대해선 이미 다룬 적이 있어서 중복되는 내용들이 있겠지만 다시 한번 정리해보도록 하겠습니다.

 

만약 우리가 객체지향 언어인 JAVA로 개발은 한다면 당연히 객체지향이라는 패러다임에 맞게 코드를 작성해 나갈 것입니다. 하지만 RDBMS(관계형 데이터베이스)를 함께 사용한다면 불편한 점이 생깁니다. 이 불편한 점들은 객체와 테이블 간의 간극에서 발생하는데요. RDMBS는 테이블 형태의 단순한 값들로만 이루어져 있어서, (보통의 경우) primary key(기본 키)라는 값으로 서로의 관계가 형성되는 반면 객체는 상호 간의 상속 혹은 참조로 관계가 형성되어 그 관계가 그래프의 형태로 나타나게 되죠. 이 둘 사이에는 크게 5 가지면에서 간극이 있습니다.

 

Granularity

상속은 객체지향에서 빠질 수 없는 개념이지만 RDBMS는 상속을 지원하지 않습니다.

 

Subtypes (inheritance)

RDB의 테이블이 객체의 모델 model과 정확하게 1:1로 매칭 되기는 어렵습니다. 물론 불가능한 것은 아니라 억지로라도 매칭을 시킬 수는 있겠지만 이렇게 되면 객체지향적이지 못한 구조가 될 수 있습니다. ex) 상속으로 표현하는 것이 나은 model

 

Identity

RDMBS는 primary key(value) 하나만으로 동일성을 판단합니다. 하지만 객체는 참조의 동일성 a == b와 값의 동일성 a.equals(b) 비교 모두를 지원합니다.

 

Associations

객체에서는 단방향 참조로만 연관성이 나 타지만 RDBMS는 foreign key (with primary key)를 사용해 양방향의 연관성을 가집니다.

 

Data navigation

특정 데이터에 접근하기 위해 객체는 한 단계씩 참조를 따라 찾아 들어가지만 RDBMS에서 이는 효율적인 방법이 아니기에 JOIN 등을 통해 쿼리를 최소화하며, 해당 데이터에 접근하지 전에 엔티티를 이미 선택합니다.

 

이런 차이점들로 인해 RDBMS는 객체지향적인 프로그래밍을 하는데 불편함을 줍니다. 이런 불편을 해소하기 위해 객체(Object)와 관계형(Relation) 데이터를 이어주는(Mapping) 기술이 바로 ORM이며 Java는 JPA를 ORM 기술의 표준 스펙으로 채택하고 있습니다.

 

 

영속성 컨텍스트(Persistence Context)

 

JPA에 대해 알기 위해 꼭 이해하고 넘어가야 하는 개념이 바로 영속성 컨텍스트입니다.

Hibernate ORM is concerned with helping your application to achieve persistence. So what is persistence? Persistence simply means that we would like our application’s data to outlive the applications process. In Java terms, we would like the state of (some of) our objects to live beyond the scope of the JVM so that the same state is available later.
Hibernate ORM은 애플리케이션이 영속성을 가지도록 돕는다. 그렇다면 영속성이란 무엇일까? 영속성은 단순히 애플리케이션의 데이터가 애플리케이션 프로세스보다 오래 지속되는 것을 의미한다. Java를 예로 들면 객체의 상태가 JVM의 범위를 벗어나서도 동일한 상태를 가질 수 있게 하는 것이다.
 -hibernate.org

Java Persistence API(자바 영속성 기술 명세)라는 이름에서도 알 수 있듯이 JPA는 Entitiy를 JVM의 실행 여부와 상관없이 영구적으로 관리할 수 있게 해주는 기술 명세이며, 영속성 컨텍스트는 Entity를 영구적으로 저장해주는 환경을 의미합니다. 즉 영속성 컨택스트에 대해 이해한다는 것은 JPA가 Entity를 관리하는 방식 혹은 메커니즘에 이해한다는 것과 같습니다.

 

EntityManager interface used to interact with the persistence context. A persistence context is a set of entity instances in which for any persistent entity identity there is a unique entity instance. Within the persistence context, the entity instances and their lifecycle are managed. The EntityManager API is used to create and remove persistent entity instances, to find entities by their primary key, and to query over entities.
EntityManager 인터페이스는 영속성 컨텍스트와 상호작용(?)하는 데 사용된다. 영속성 컨텍스트는 모든 영속성 엔티티 ID에 대해 고유한 엔티티 인스턴스가 있는 엔티티 인스턴스 set입니다. 지속성 컨텍스트 내에서 엔터티 인스턴스와 해당 수명 주기가 관리됩니다. EntityManager API는 영구 엔터티 인스턴스를 생성 및 제거하고 기본 키로 엔터티를 찾고 엔터티를 쿼리 하는 데 사용됩니다.
 - docs.oracle

영속성 컨텍스트는 EntityManager를 통해 사용할 수 있습니다. J2SE 환경에서 EntityManger가 생성될 때마다 거기에 해당하는 영속성 컨택스트가 생성되어 서로 1:1의 관계를 가집니다. 하지만 J2EE 환경에서는 다수의 EntityManger가 있더라도 영속성 컨텍스트는 하나로 유지돼 N:1의 관계를 가집니다.

 

 

 

Entity 생명주기

 

영속성 컨택스트에 대해 더 자세히 알아보기 전에 Entitiy의 생명주기에 대해 먼저 짚고 넘어가보겠습니다. Entity는 다음과 같은 생명주기를 가집니다.

  • 비영속(new/transient)  영속성 컨텍스트와 전혀 관계 없는 엔티티
  • 영속(managed)             영속성 컨텍스에 의해 관리되고 있는 엔티티
  • 준영속(detached)         영속성 컨텍스트에 저장되었다가 분리된 엔티티
  • 삭제(removed)              삭제된 상태

Entity의 생명주기

 
//비영속 상태
//비영속 상태의 엔티티이기 때문에 어떤 변화가 일어나더라도
//영속성 컨텍스트는 아무일도 하지 않는다.
Foo foo = new Foo();
foo.setId(123L); 

EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();

//영속 상태
//persist method로 엔티티를 영속상태로 만들어 준다.
em.persist(foo);

//준영속 상태
//detach method로 엔티티를 영속성 컨텍스트에서 분리시킨다.
em.detach(foo);

//삭제 상태
em.remove(foo);

EntityManager는 entity의 생명주기(상태)를 관리해줍니다. persist로 영속성 컨텍스트가 entity를 관리하도록 등록해주고, entity는 영속(managed)상태가 되며 영속 상태의 entity의 변화를 감지해 database에 업데이트 쿼리를 날려줍니다. 영속상태의 entity는 detach로 영속성 컨텍스트로 부터 분리시킬 수 있고, remove로 아예 delete시킬 수도 있습니다.

 

이처럼 영속성 컨택스트는 영속상태의 entity의 변화를 추적합니다. 만약 영속된 상태의 entity에 변화가 일어났을 경우 자동으로 database에 그 변화를 반영하는 것입니다. 이렇게 영속성 컨텍스트를 사용할 경우 강력한 기능들로 인해 여러 이점들을 취할 수 있습니다.

 

 

영속성 컨텍스트(Persistence Context)의 장점

 

1차 캐시(First-level cache)

영속성 컨텍스트는 내부에 1차 캐시라는 것을 이용해 엔티티를 관리합니다. '영속성 컨텍스트는 곧 1차 캐시다' 라고 이해해도 무방할 정도로 영속성 컨텍스트의 절대적으로 중요한 역할을 하고 있습니다.

The persistence context is the first-level cache ...
영속성 컨텍스트는 1차 캐시다 ...
- baeldung.com
//비영속 상태
Foo foo = new Foo();
foo.setId(123L); 

//영속 상태
em.persist(foo);

// database가 아닌 1차 캐시에서 조회
Foo findFoo = em.find(Foo.class, 123L);

1차 캐시는 말그대로 어플리케이션과 db사이에서 엔티티를 관리하는 캐시입니다. 예제처럼 엔티티를 조회하거나 했을 때 JPA는 db에서 조회를 하기 전에 1차 캐시에 필요한 엔티티가 있는지 먼저 찾습니다. 따라서 예제의 경우 select 쿼리가 발행되지 않으며 1차 캐시에 있는 엔티티를 반환하게 됩니다.

만약 1차 캐시에 원하는 엔티티가 없을 경우 db에서 조회 후 조회한 엔티티를 1차 캐시에 등록, 등록된 엔티티를 반환합니다.

 

 

동일성 보장

Foo foo1 = em.find(Foo.class, 123L);
Foo foo2 = em.find(Foo.class, 123L);

foo1 == foo2 //true

순수 자바에선 아무리 같은 값을 가진 엔티티라고 해도 그 레퍼런스가 같진 않습니다. foo1과 foo2는 같은 값을 가지고 있는 서로 다른 객체가 되는 것입니다. 하지만 JPA는 같은 트렌젝션 안에서 동일한 엔티티를 조회할 경우 레퍼런스의 동일함 까지도 보장해 줍니다. 이는 1차 캐시가 있기 때문에 가능한 것이며, 앞서 이야기 한 것 처럼 1차 캐시에 있는 엔티티 하나를 두 번 꺼낸 것일 뿐이니 조금만 생각해 본다면 쉽게 이해할 수 있습니다.

 

 

쓰기지연 (Transactional write-behind)

JPA는 영속상태의 엔티티의 모든 변화를 감지하고, DB에 반영합니다. 하지만 엔티티를 만들 때 마다 혹은 만들어진 엔티티에 변경이 일어날 때마다 쿼리가 날아간다면 성능 저하의 우려가 있을 겁니다. 엔티티의 여러가지 값들을 변경하고 update를 하려고 하는데, 값들 하나 하나 변경될 때마다 update쿼리가 날아간다면 얼마나 많은 쿼리가 발생될까요.

 

따라서 JPA는 영속상태의 엔티티에 변화가 생기거나 새로운 엔티티가 persist 된다고 하더라도 바로 쿼리를 날리지 않습니다. 엔티티의 변화가 일어날 때마다 쿼리문을 생성하는것은 맞지만 이는 쓰기 지연 SQL 저장소 라는 곳에 저장되고 transaction.coomit()을 하거나 직접 EntityManager의 flush를 호출하면 저장소의 쿼리들이 flush 되면서 DB로 날아가게 됩니다. 쿼리가 버퍼 사이즈 만큼 차오르면 자동으로 flush를 하기도 합니다.

 

변경 감지(Dirty checking)

지금 까지 계속 이야기 해온 부분이지만 JPA는 영속상태 엔티티의 모든 변경을 감지합니다. 단순히 set만 해도 그 변경사항이 DB에 반영이 되는 것입니다.

EntityManager가 flush되면 영속 상태의 엔티티와 그 엔티티의 스냅샷을 비교합니다. 스냅샷에는 엔티티가 1차 캐시에 들어갈 때의 상태가 저장되어 있어 이 둘의 비교로 변경을 감지 할 수 있는 것입니다.

 

 

ref.

자바 ORM 표준 JPA 프로그래밍 - 기본편

'jpa' 카테고리의 다른 글

[JPA] Entity 연관관계  (0) 2022.01.06
[JPA] @Entity(엔티티 매핑)  (0) 2021.12.24
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함