본문 바로가기
개발 지식/JPA

[JPA] JPA가 무엇일까

by 에르주 2021. 12. 26.
반응형

실무에서 JPA ORM을 활용하며 개발을 진행하고 있다. 
야생형으로 개발을 진행하고 있는데 개발 시작 후 1년이 지난 지금 조금은 JPA에 익숙해졌다 느껴졌을 때 인프런 강의를 바탕으로 JPA를 관련 된 내용을 조금씩 정리해보려고 한다.

JPA는 Java Persistence API 의 약자로 ORM(Object-relational mapping)기술을 활용하고 있다.
1. 즉 자바의 객체와 데이터베이스의 값들을 매핑하는 것이다.

Mybatis는

	<insert id="insert_1" parameterType="hashmap">
		insert into tab_erj (id, name, number) value( #{id}...)       
	</insert>

위의 예시 mapper.xml 처럼 mapper에 ID 및 데이터 베이스 방언(쿼리)들을 사용하여 직접 데이터를 CRUD를 했다면

JPA는

@Entity
@Table(name="table_er")
public class erEntity {

@Id
private String id;
private String name;
private String number;

}

Entity의 클래스 객체를 선언 및 각각의 칼럼값들을 String 형태로 선언하면 JPA는 해당하는 "table_er" 데이터 베이스의 테이블과 매핑 시켜준다.

즉 개발자는 DB 쿼리에 신경쓰기보다는 객체 자체에 집중하게 되어 개발 생산성이 향상 될 수 있다.

객체 형태로 매핑된다는 것은 
객체 지향의 '상속'을 사용할 수 있는 것이고 이것은 데이터 베이스의 연관관계로 연결되어 테이블간 Join까지 나타 낼 수 있으며 

후에 나오는 영속성 컨텍스트를 활용하게 되면 자바의 컬렉션(List 등)에서 리스트 값을 조회하고 수정하는 것과 같이 데이터 베이스의 데이터를 다루고 활용할 수 있다는 것이다.


JPA에서 가장 중요한 것이 또 있다. 
2. 영속성 컨텍스트 (Persistence Context)

지속적 문맥 이라고 직역 할 수 있는데 지속적인 데이터 즉 사라지지 않는 데이터를 의미 한다.
영속성 컨텍스트를 이해하기 위해서는 EntityManager라는 것을 알아야 한다.

EntityManager란 말 그대로 Entity(테이블의 객체)를 관리하는 것이며 ThreadFactory 또는 sqlSessionFactory처럼 EntityManagerFactory에서 생성된다. 

그리고 요청에 따라 Factory에서 EntityManager가 생성하고 이를 DB와의 커넥션에 사용한다.

또한 EntityManger를 통해 영속성 컨텍스트에 접근하게 되는데 영속성 컨텍스트 안에 있는 Entity의 생명주기는 다음과 같다.

 

위의 그림에서 볼 수 있 듯 생명주기는 4개로 나뉜다.

1. 비영속(New/transient)

: 영속성 컨텍스트와 전혀 관계 없는 새로운 상태

비영속 상태는 자바에서 객체 생성하는 것과 똑같다

Member member = new Member();
member.setId("minsupark");
member.setUsername("민수");

 

2. 영속(managed)

: 영속성 컨텍스트에 관리되는 상태

tx.begin();

Member member = new Member();
member.setId(100L);
member.setUserName("minsupark");
System.out.println("==== Member 객체 생성 완료 ====");

em.persist(member);
System.out.println("==== Member 객체 생성 영속성 상태 ====");

tx.commit(); // Entity Transaction commit

 

In`

콘솔창에 쿼리가 찍힌 것을 확인해보면 
em.persist(member); 
-> EntityManager에 영속성 상태로 저장된다고 해도 사실 Transaction이 실행되는 부분은 commit 부분인 것을 확인할 수 있다.

Member 테이블 데이터 Insert



3. 준영속(Detached)

: 영속성 컨텍스트에 저장되었다가 분리된 상태 

try{

  member.setId(100L);
  member.setUserName("minsupark");
  System.out.println("==== Member 객체 생성 완료 ====");
  em.persist(member);
  System.out.println("==== Member 객체 생성 영속성 상태 ====");

  //            tx.commit(); // Entity Transaction commit
  }
  catch (Exception e) {
 	 tx.rollback();
  }


  em.detach(member);
  System.out.println("==== Member 객체 생성 영속성 분리 ====");
  tx.commit();

commit을 detach(분리) 후 실행하게 되면

insert 쿼리 어디?

EntityManager에 담긴 Member 객체가 분리되어 Insert 쿼리가 실행되지 않는 것을 확인할 수 있다.


4. 삭제(removed)

: 해당 객체의 삭제

        try {

            member.setId(120L);
            member.setUserName("Erjuer");
            System.out.println("==== Member 객체 생성 완료 ====");
            em.persist(member);
            System.out.println("==== Member 객체 생성 영속성 상태 ====");

//            tx.commit(); 
        }
        catch (Exception e) {
            tx.rollback();
        }

        System.out.println("==== Before member removed ====");
        em.remove(member);
        tx.commit(); // Entity Transaction commit

        System.out.println("==== After member removed ====");

해당 코드와 같이 
EntityManager에 member객체를 삭제 한다는 것은 다음과 같은 쿼리로 확인 할 수 있다. 
-> 객체를 삭제한다는 것은 EntityManager에 관리되고 있는 데이터(객체)를 DB에서 지우겠다는 뜻이다.

Insert And Delete 쿼리&amp;nbsp;


그럼 영속성 컨텍스트의 장점은 무엇일까?
실무에서 JPA를 활용한 CUD 부분을 진행하면서 같은 파트 개발자 팀원분의 주석을 보게 되었다.

// 영속성 컨텍스트를 활용한다. 

사실 이 주석을 보고 "1차 캐시 그리고 변경 감지(dirty Check)등 여러가지 이유"라는 생각은 났지만 더 정확히 이해하고 싶어 내용을 정리하고자 한다.

장점은

  • 1차 캐시
  • 동일성 보장
  • 트랜잭션을 지원하는 쓰기 지연
  • 변경 감지
  • 지연 로딩

으로 총 5가지를 볼 수 있으며 하나씩 무슨 내용인지 살펴보고자 한다.

1. 1차 캐시

'캐시(Cache)' : 컴퓨터 과학에서 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다. (feat. wiki)
말 그대로 '1차 캐시'는 1차 임시 저장소를 가리킨다.

EntityManager(em)의 영속성 컨텍스트에 관리되고 있는 Member를 Find 할 시
DB 트랜잭션이 발생하는 것이 아니라 PK(100L)에 맞는 데이터를 1차 캐시에서 불러온다.

그리고 나서 Insert Query가 실행된다.

  member.setId(100L);
  member.setUserName("misupark");

  System.out.println(" === 영속성 Before === ");
  em.persist(member);
  System.out.println(" === 영속성 After === ");


  Member findMember = em.find(Member.class, 100L);

  System.out.println("==== findMember ID ==== : "  + findMember.getId());
  System.out.println("==== findMember UserName ==== : "  + findMember.getUserName());

  tx.commit();

 

 

2. 동일성 보장

1차 캐시로 저장된 엔티티 값을 반복 가능한 읽기 (반복 호출, ex. PK : 100L)는 데이터베이스가 아닌 애플리케이션에서의 동일성을 보장한다.

  member.setId(100L);
  member.setUserName("misupark");

  System.out.println(" === 영속성 Before === ");
  em.persist(member);
  System.out.println(" === 영속성 After === ");


  Member findMember1 = em.find(Member.class, 100L);
  Member findMember2 = em.find(Member.class, 100L);


  System.out.print("==== findMember equal ==== : ");
  System.out.println( findMember1 == findMember2);

동일성 보장

3. 트랜잭션을 지원하는 쓰기 지연

EntityManager(em)의 영속성 컨텍스트에 member1, member2의 객체를 등록하더라도 그 즉시 Insert Query가 발생하는 것이 아닌 commit 시 2개의 Insert 쿼리가 발생한다.
즉, 쓰기 지연이 발생한다. 쓰기 지연 SQL 저장소에 SQL 쿼리를 생성해놓고 commit시 DB의 Transaction이 발생한다.

  Member member1 = new Member();
  member1.setId(100L);
  member1.setUserName("misupark");


  System.out.println(" === 영속성 member1 Before === ");
  em.persist(member1);
  System.out.println(" === 영속성 member1 After === ");


  Member member2 = new Member();
  member2.setId(101L);
  member2.setUserName("ErJuer");

  System.out.println(" === 영속성 member2 Before === ");
  em.persist(member2);
  System.out.println(" === 영속성 member2 After === ");


  em.persist(member1);
  em.persist(member2);
  // Insert 쿼리가 발생하지 않는다.



  tx.commit(); // commit 시 Transaction 및 Insert 쿼리

쓰기 지연 트랜잭션 (Insert가 commit 후 동시에 수행된다.)



4. 엔티티 수정

엔티티 수정이 실무에서 CUD시 영속성 컨텍스트를 사용했던 주 이유이기도 하다.
데이터 값을 수정시 update 메소드 또는 관련된 Transaction이 수행되는 것이 아니라  commit시 JPA가 변경을 감지하고 Transcation을 수행하게 된다.

  Member member1 = new Member();
  member1.setId(100L);
  member1.setUserName("misupark");
  em.persist(member1);

  System.out.println(" ======= member1 영속성 객체 등록 ======= ");
  Member findmember = em.find(Member.class,100L); // 영속 엔티티 조회
  findmember.setUserName("X-MAS!!"); // 영속 엔티티 데이터 수정
  System.out.println(" ======= member1 영속성 객체 수정 ======= ");
  
  tx.commit(); // commit 시 Transaction 및 Insert 쿼리

엔티티 수정


Username이 "minsupark"에서 "X-MAS!!"로 변경시 따로 update 쿼리가 없어도 commit시
Select와 Update 쿼리가 실행된 것을 확인 할 수 있다.

이를 변경감지(Dirty Checking)이라고도 한다.

끝.


reference)
https://www.inflearn.com/course/ORM-JPA-Basic

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., 본 강의는 자바 백엔

www.inflearn.com

 

반응형

댓글