티스토리 뷰

Spring/Spring Data

영속성 컨텍스트

woo'^'chang 2022. 7. 5. 22:44

JPA

SQL 중심적인 개발을 진행하다 보니 다음과 같은 문제를 확인할 수 있었습니다.

  • 반복적이고 지루한 작업
  • 객체와 테이블 사이의 패러다임 불일치

문제를 해결하기 위해 객체를 자바 컬렉션에 저장하듯 DB에 저장할 수 없을까 하는 생각으로 새로운 방법을 찾게 되었고 JPA가 등장하게 되었습니다.

 

JPA(Java Persistence API)는 자바 진영의 ORM 표준 기술로 애플리케이션과 DB 사이에서 동작하게 됩니다. Java 코드로 JPA에 접근하게 되고 JPA는 내부적으로 JDBC API를 이용해 DB에 접근합니다.

JPA를 간단히 말하면 인터페이스의 모음입니다. 기존 JPA 없이 JDBC API를 사용할 때는 SQL을 다음과 같이 일일이 작성해야 했습니다.

public Member createMember(String firstName, String lastName) {
    Connection con = connection();
    PreparedStatement ps = null;
    ResultSet rs = null;
    Member member = null;
    try {
        String sql = "insert into members(first_name, last_name) values(?, ?)";
        ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        ps.setString(1, firstName);
        ps.setString(2, lastName);
        ps.executeUpdate();
        rs = ps.getGeneratedKeys();
        if (rs.next()) {
            member = new Member(rs.getInt(1), firstName, lastName);
        }
        closeConnection(con);
        disconnect(ps, rs);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return member;
}

하지만 JPA를 사용하게 되면 다음과 같이 코드를 작성할 수 있습니다.

public Member createMember(String firstName, String lastName) {
    Member member = new Member(firstName, lastName);
    em.persist(member);
    return member;
}

SQL을 작성한 부분도 없으며 객체를 객체답게 저장하고 있는 느낌을 줍니다.

 

이를 이해하기 위해서는 ORM(Object Relational Mapping) 기술을 먼저 이해해야 합니다. 객체지향 언어와 관계형 데이터베이스로 구축된 프로젝트들은 둘 사이의 패러다임을 해결이 필요합니다. ORM은 객체는 객체답게, 관계형 데이터베이스는 관계형 데이터베이스답게 설계하자는 의미를 담고 있습니다.

ORM 프레임워크는 애플리케이션과 DB 사이에서 Mapper 역할을 수행하게 되고 애플리케이션 개발자는 객체 지향 개발에 집중할 수 있고 DB 설계자는 테이블 설계에 집중할 수 있게 됩니다. ORM을 이해하셨다면 자바 진영의 ORM 표준 기술인 JPA라는 말도 이해하셨을 것으로 생각합니다.

 

JPA를 도입함으로 SQL 중심적인 개발에서 객체 중심적인 개발로 바뀌게 되었고, 이에 따라 생산성, 유지보수, 성능, 표준화 등의 지표도 성장하게 되었습니다. 또한 근본적인 문제였던 패러다임 불일치 문제도 해결할 수 있게 되었습니다.

영속성 컨텍스트

JPA에 대해 기본적으로 알게 되었으니 JPA의 가장 핵심적인 내용 중 하나인 영속성 컨텍스트에 대해 알아보겠습니다.

 

영속성 컨텍스트란 엔티티를 영구 저장하는 환경을 의미합니다. 엔티티는 사물의 구조, 상태, 동작을 모델로 표현했을 때 모델의 구성 요소를 의미하는데 테이블에 저장되는 하나의 데이터라고 해석하시면 됩니다.

 

DB에 데이터는 저장될 텐데 이런 저장 환경이 왜 필요한지에 대한 의문을 가질 수 있습니다. 예를 들어 SQL 개별로 계속 DB와의 네트워크를 사용하게 되면 네트워크 비용이 증가하게 됩니다. DB 변화 수행 단위인 트랜잭션만큼 모아두었다가 한 번의 네트워크만 거치게 된다면 그만큼 네트워크 비용을 줄일 수 있는 장점이 존재합니다.

 

그 외에도 1차 캐시, 동일성 보장, 변경 감지, 지연 로딩 등 많은 장점이 존재하는데 하나씩 알아보도록 하겠습니다.

엔티티의 생명주기

영속성 컨텍스트의 엔티티는 생명주기를 가지고 있습니다. 생명주기에 따라 엔티티의 상태가 결정됩니다.

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

그림과 같이 보며 아래의 내용을 따라가면 이해하기 쉽습니다.

비영속(New/Trasient)

영속성 컨텍스트와 아무 관계 없는 상태를 의미합니다. 객체를 단순히 생성만 한 상태라고 볼 수 있습니다.

Member member = new Member();
member.setFirstName("Choi");
member.setLastName("woochang");
영속(Managed)

객체가 영속성 컨텍스트의 엔티티로 저장된 상태를 의미합니다. 저장된 엔티티는 영속성 컨텍스트에 의해 관리됩니다. 영속성 컨텍스트를 관리하는 엔티티 매니저가 존재하는데 이는 엔티티 매니저 팩터리로부터 생성할 수 있습니다.

 

DB에서 데이터를 불러오는 find나 직접 쿼리를 작성하는 JPQL에 의해서도 영속 상태가 될 수 있습니다.

EntityManager em = entityManagerFactory.createEntityManager();
// 영속
em.persist(member);
준영속(Detached)

영속성 컨텍스트에 저장되어 있던 엔티티가 영속성 컨텍스트로부터 분리된 상태를 의미합니다. 영속성 컨텍스트를 비우는 clear나 영속성 컨텍스트 자원을 종료하는 close에 의해서도 분리됩니다.

em.detach(member);
삭제(Removed)

실제 DB에 해당 데이터 삭제를 요청하는 상태를 의미합니다.

em.remove(member);

영속성 컨텍스트의 이점

1차 캐시

영속성 컨텍스트 내부에는 아래의 그림과 같이 엔티티를 저장하는 Map이 존재합니다.

  • @Id : 엔티티를 구분하는 값으로 해당 테이블의 PK 값을 가지게 됩니다.
  • Entity : 영속성 컨텍스트가 관리하는 엔티티가 저장됩니다.

em.find()를 통해 찾고자 하는 엔티티 정보를 불러올 수 있는데, 영속성 컨텍스트에 해당 엔티티가 존재한다면 DB 조회를 거치지 않고 바로 가져오게 됩니다. 존재하지 않는다면 DB 조회를 거쳐 영속성 컨텍스트에도 저장 후 가져오게 되는데 트랜잭션이라는 작은 작업 단위에서 존재하는 캐시이기 때문에 큰 이점은 존재하지 않습니다.

동일성 보장

앞서 말씀드린 1차 캐시로 인해 ID로 구별되는 엔티티들을 영속성 컨텍스트에 저장하고 있기에 자바 컬렉션의 객체처럼 ==로 비교했을 때 동일성이 보장됩니다.

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

내부적으로 쓰기 지연 SQL 저장소를 가지고 있으므로 트랜잭션 단위로 SQL을 저장해 두었다 한 번에 실행시켜 네트워크를 타게 되는 횟수를 줄입니다. hibernate.jdbc.batch_size를 지정해 저장되는 크기를 변경할 수 있습니다.

변경 감지

실질적으로 DB에 변경이 진행되는 commit 이전에 flush 동작을 진행하게 되는데 flush에서는 다음 동작을 수행합니다.

  • 영속성 컨텍스트에 처음 저장될 때의 스냅샷과 비교 후 변경점이 존재한다면 update 쿼리를 쓰기 지연 SQL 저장소에 저장합니다.
  • 쓰기 지연 SQL 저장소에 저장된 SQL문의 실행을 시작합니다.
  • flush가 끝나더라도 1차 캐시는 그대로 유지됩니다.

이러한 flush로 인해 수정 후 별도의 저장 동작을 수행하지 않아도 자동으로 변경이 감지되어 DB 변경이 발생합니다. 아래의 코드만으로 변경을 진행할 수 있습니다.

member.setFirstName("Kim");

참고

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

 

'Spring > Spring Data' 카테고리의 다른 글

[JPA] 페치 조인(fetch join)이란?  (4) 2022.07.29
[Error] 엔티티 인식 에러 해결  (0) 2022.07.29
[JPA] 프록시와 로딩 전략  (0) 2022.07.20
JPA 연관관계 매핑 정리  (0) 2022.07.13
댓글
최근에 올라온 글
최근에 달린 댓글
«   2024/09   »
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
Total
Today
Yesterday