[프로그래밍 기록] Spring - JPA, 영속성 컨텍스트

김엄지·2024년 3월 24일
0

Spring

목록 보기
15/21

ORM이란?

  • Object-Relational Mapping, 객체와 DB의 관계를 매핑 해주는 도구
  • 자바의 클래스와 DB의 데이터를 ORM을 사용하면 이를 자동으로 처리해준다.

JPA란?

  • JPA: Java Persistence API, 자바 ORM 기술에 대한 표준 명세

  • JPA는 애플리케이션과 JDBC 사이에서 동작되고 있다.
  • JPA를 사용하면 DB 연결 과정을 직접 개발하지 않아도 자동으로 처리해준다.
  • 또한 객체를 통해 간접적으로 DB 데이터를 다룰 수 있기 때문에 매우 쉽게 DB 작업을 처리할 수 있다.

하이버네이트(Hibernate)란?

  • JPA 는 표준 명세이고, 이를 실제 구현한 프레임워크 중 사실상 표준이 하이버네이트
  • Spring Boot에서는 기본적으로 ‘하이버네이트’ 구현체를 사용 중이다.

Entity란?

  • JPA에서 관리되는 클래스 즉, 객체를 의미
  • Entity 클래스는 DB의 테이블과 매핑되어 JPA에 의해 관리된다.

[SpringBoot 환경에서의 JPA 설정]

build.gradle

// JPA 설정
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// MySQL
implementation 'mysql:mysql-connector-java:8.0.28'

application.properties : Hibernate 설정

spring.jpa.hibernate.ddl-auto=update

spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
[ddl-auto]
- create : 기존 테이블 삭제 후 다시 생성합니다. (DROP + CREATE)
- create-drop : create와 같으나 종료시점에 테이블을 DROP 합니다.
- update : 변경된 부분만 반영합니다.
- validate : Entity와 테이블이 정상 매핑되었는지만 확인합니다.
- none: 아무것도 하지 않습니다.

[show_sql, format_sql, use_sql_comments 옵션]
- Hibernate가 DB에 요청하는 모든 SQL을 보기좋게 출력 해줍니다.

Entity

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    // 테이블의 기본 키를 지정
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // // auto_increment 조건
    private Long id;

    // nullable: null 허용 여부
    // unique: 중복 허용 여부 (false 일때 중복 허용)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    // length: 컬럼 길이 지정
    @Column(name = "contents", nullable = false, length = 500)
    private String contents;
}
  • 기본 키는 영속성 컨텍스트에서 Entity를 구분하고 관리할 때 사용되는 식별자 역할을 수행
  • 식별자 값을 넣어주지 않고 저장하면 오류가 발생
  • @GeneratedValue 옵션을 추가하면 기본 키 생성을 DB에 위임할 수 있다.

영속성 컨텍스트란?

Persistence를 한글로 번역하면 영속성, 지속성 이라는 뜻

  • 객체가 유지되는 시간이나 객체의 위치을 자유롭게 유지하고 이동할수 있는 객체의 성질

  • Entity 객체를 효율적으로 쉽게 관리하기 위해 만들어진 공간

  • JPA는 영속성 컨텍스트에 Entity 객체들을 저장하여 관리하면서 DB와 소통한다.

  • 영속성 컨텍스트에 접근하여 Entity 객체들을 조작하기 위해서는 EntityManager가 필요

  • 개발자들은 EntityManager를 사용해서 Entity를 저장하고 조회하고 수정하고 삭제할 수 있다.

  • EntityManager는 EntityManagerFactory를 통해 생성하여 사용할 수 있다.

  • EntityManagerFactory는 일반적으로 DB 하나에 하나만 생성되어 애플리케이션이 동작하는 동안 사용된다.

  • EntityManagerFactory를 만들기 위해서는 DB에 대한 정보를 전달해야헌다.

  • 정보를 전달하기 위해서는 /resources/META-INF/ 위치에 persistence.xml 파일을 만들어 정보를 넣어두면 된다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.2"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd">
    <persistence-unit name="memo">
        <class>com.sparta.entity.Memo</class>
        <properties>
            <property name="jakarta.persistence.jdbc.driver" value="com.mysql.cj.jdbc.Driver"/>
            <property name="jakarta.persistence.jdbc.user" value="root"/>
            <property name="jakarta.persistence.jdbc.password" value="{비밀번호}"/>
            <property name="jakarta.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/memo"/>

            <property name="hibernate.hbm2ddl.auto" value="create" />

            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.use_sql_comments" value="true"/>
        </properties>
    </persistence-unit>
</persistence>

👍 SpringBoot 환경에서는

  • EntityManagerFactory와 EntityManager를 자동으로 생성해준다.
  • application.properties에 DB 정보를 전달해 주면 이를 토대로 EntityManagerFactory가 생성된다.
  • @PersistenceConext 애너테이션을 사용하면 자동으로 생성된 EntityManager를 주입받아 사용할 수 있다.

JPA의 트랜잭션이란?

트랜잭션이란?

트랜잭션은 DB 데이터들의 무결성과 정합성을 유지하기 위한 하나의 논리적 개념

  • 모든 SQL이 성공적으로 수행이 되면 DB에 영구적으로 변경을 반영하지만 SQL 중 단 하나라도 실패한다면 모든 변경을 되돌린다.

JPA의 트랜잭션

  • JPA는 DB의 이러한 트랜잭션 개념을 사용하여 효율적으로 Entity를 관리한다.

  • DB에서 하나의 트랜잭션에 여러 개의 SQL을 포함하고 있다가 마지막에 영구적으로 변경을 반영하는 것 처럼

  • JPA에서도 영속성 컨텍스트로 관리하고 있는 변경이 발생한 객체들의 정보를 쓰기 지연 저장소에 전부 가지고 있다가 마지막에 SQL을 한번에 DB에 요청해 변경을 반영한다.

Spring의 트랜잭션

  • Spring 프레임워크에서는 DB의 트랜잭션 개념을 애플리케이션에 적용할 수 있도록 트랜잭션 관리자를 제공한다.
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
						...
			
		@Transactional
		@Override
		public <S extends T> S save(S entity) {
		
			Assert.notNull(entity, "Entity must not be null");
		
			if (entityInformation.isNew(entity)) {
				em.persist(entity);
				return entity;
			} else {
				return em.merge(entity);
			}
		}

						...
}
  • @Transactional 애너테이션을 클래스나 메서드에 추가하면 쉽게 트랜잭션 개념을 적용할 수 있다.
  • 메서드가 호출되면, 해당 메서드 내에서 수행되는 모든 DB 연산 내용은 하나의 트랜잭션으로 묶인다.
  • readOnly = true 옵션 : 트랜잭션에서 데이터를 읽기만 할 때(조회의 경우) 사용된다.

영속성 컨텍스트의 기능

1차 캐시

영속성 컨텍스트는 내부적으로 캐시 저장소를 가지고 있다.

  • 우리가 저장하는 Entity 객체들이 1차 캐시 즉, 캐시 저장소(Map 자료구조 형태)에 저장된다.
    • key에는 @Id로 매핑한 기본 키 즉, 식별자 값을 저장
    • value에는 해당 Entity 클래스의 객체를 저장
  • 영속성 컨텍스트는 캐시 저장소 Key에 저장한 식별자값을 사용하여 Entity 객체를 구분하고 관리한다.

'1차 캐시' 사용의 장점
1. DB 조회 횟수를 줄임
2. '1차 캐시'를 사용해 DB row 1개 당 객체 1개가 사용되는 것을 보장 (객체 동일성 보장)

Entity 저장

  • em.persist(memo); 메서드가 호출되면 memo Entity 객체를 캐시 저장소에 저장

Entity 조회

  • 캐시 저장소에 조회하는 Id가 존재하지 않은 경우
    1) 캐시 저장소 조회

    2) DB SELECT 조회 후 캐시 저장소에 저장

  • 캐시 저장소에 조회하는 Id가 존재하는 경우
    호출 시 캐시 저장소에 식별자 값이 1이면서 Memo Entity 타입인 값이 있는지 조회
    값이 있다면 해당 Entity 객체를 반환

Entity 삭제

  • 삭제할 Entity를 조회한 후 캐시 저장소에 없다면 DB에 조회해서 저장

  • em.remove(memo); 호출 시 삭제할 Entity를 DELETED 상태로 만든 후 트랜잭션 commit 후 Delete SQL이 DB에 요청

쓰기 지연 저장소(ActionQueue)

  • JPA가 트랜잭션 처럼 SQL을 모아서 한번에 DB에 반영한다
  • JPA는 이를 구현하기 위해 쓰기 지연 저장소를 만들어 SQL을 모아두고 있다가 트랜잭션 commit 후 한번에 DB에 반영한다.
  • flush 메서드는 영속성 컨텍스트의 변경 내용들을 DB에 반영하는 역할을 수행한다.

변경 감지(Dirty Checking)

JPA에서는 Update를 어떻게 처리할까? 아래와 같은 과정은 변경 감시라고 부른다.

  • JPA는 영속성 컨텍스트에 Entity를 저장할 때 최초 상태(LoadedState)를 저장한다.
    - 트랜잭션이 commit되고 em.flush(); 가 호출되면 Entity의 현재 상태와 저장한 최초 상태를 비교
    - 변경 내용이 있다면 Update SQL을 생성하여 쓰기 지연 저장소에 저장하고 모든 쓰기지연 저장소의 SQL을 DB에 요청
    - 마지막으로 DB의 트랜잭션이 commit 되면서 반영
  • 변경하고 싶은 데이터가 있다면 먼저 데이터를 조회하고 해당 Entity 객체의 데이터를 변경하면 자동으로 Update SQL이 생성되고 DB에 반영된다.

영속성 컨텍스트와 트랜잭션의 생명주기

  • 스프링 컨테이너 환경에서는 영속성 컨텍스트와 트랜잭션의 생명주기가 일치한다.
  • Spring에서는 트랜잭션을 제어할 수 있도록 트랜잭션 전파 기능을 제공하고 있다.
  • 트랜잭션 전파의 기본 옵션 : REQUIRED
    REQUIRED 옵션은 부모 메서드에 트랜잭션이 존재하면 자식 메서드의 트랜잭션은 부모의 트랜잭션에 합류하게된다.
profile
나만의 무언가를 가진 프로그래머가 되자

0개의 댓글