** 자기 공부용 및 암기용으로 포스팅하는 글 입니다.
** 정확한 정보는 보장 할 수 없으니, 참고용으로만 봐주시면 감사하겠습니다.
** 제 개인적인 생각 및 자기 암시목적으로 작성되기에 이점 양해 부탁드립니다.
들어가기에 앞서..
최근 간단한 게시판 솔로 프로젝트를 진행하던 중 영속성 컨텍스트 때문에 시간을 많이 잡아 먹었던적이 있다.
결국은 트렌잭션을 분리시켜 트러블슈팅을 하였다..
이후 또 다른 실수를 번복하지 않도록 내용을 정리하는 겸 포스팅을 남긴다..
★ 알아볼 내용
- 영속성 컨텍스트( Persistence Context ) 란?
- JPA API를 사용하기 위한 사전 준비
- 영속성 컨텍스트에 엔티티 저장하기
- 영속성 컨텍스트와 테이블에 엔티티 저장하기
내용 설명
1. 영속성 컨텍스트( Persistence Context )란?
JPA라는 용어에서 P의 정의는 Persistence이다.
영속성/지속성이라는 의미를 가지고 있으며 엔티티 객체 정보를 금방 사라지게 하지 않고 오래 지속되게 한다는 의미로 사용이 된다.
기본적으로 ORM은 객체(Object)와 데이터베이스 테이블의 매핑을 통하여 엔티티 클래스 객체 안에 포함된 정보를 테이블에다가 저장하는 기술이다.
JPA에서는 테이블과 매핑되는 엔티티 객체 정보를 영속성 컨텍스트(Persistence Context)라는 곳이 보관해서 애플리케이션 내에서 오래 지속되도록 한다.
영속성 컨텍스트는 이해하기 힘들 수 있다. 그 이유는 눈에 보이지 않는 논리적인 개념이기 때문이다.
이렇게 보관된 엔티티 정보는 데이터베이스 테이블에 데이터를 저장, 수정, 조회, 삭제하는데 사용하는데
잘 이해하지 못하고 사용하는 경우 데이터베이스에서 데이터를 저장/수정/조회/삭제 하려고 하였으나 영속선 컨텍스트에 남아 있어 제대로 처리가 안되는 현상이 발생 할 수 있다.
영속성 컨텍스트를 보다 잘 이해할 수 있도록 그림으로 표현해보자.
그림과 같이 1차 캐시 영역와 쓰기 지연 SQL 저장소라는 영역이 있다.
엔티티 정보를 영속성 컨텍스트에 저장(persist)하는 API를 사용하면 영속성 컨텍스트의 1차 캐시에 엔티티 정보가 저장된다.
예시 코드를 적용하기 전 우선 JPA API를 사용하기 위해 필요한 사전 준비가 있다.
2. JPA API를 사용하기 위한 사전 준비
우선 build.gradle설정을 해야한다.
dependencies에서 우리는 하나의 라이브러리 코드를 추가해야한다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // <- 추가
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
그 다음 application.yml 파일을 수정해야 한다 들여쓰기를 유의하여 작성하도록 하자.
spring:
h2:
console:
enabled: true
path: /h2
datasource:
url: jdbc:h2:mem:test
jpa:
hibernate:
ddl-auto: create # <- 추가 : 스키마 자동 생성
show-sql: true # <- 추가 : SQL 쿼리 출력
영속성 컨텍스트에 저장할 엔티티들을 아래와 같이 선언해준다.
import lombok.Getter;
import javax.persistence.*;
@Getter
@Setter
@NoArgsConstructor
@Entity // (1) 엔티티라는 것을 명시 -> bean
public class Member {
@Id // (2) 해당 값이 ID 값이라는 것을 명시
@GeneratedValue // (3) 식별자 생성 전략 명시
private Long memberId;
private String email;
public Member(String email) {
this.email = email;
}
}
이후 우리가 학습을 통해 하나씩 적어갈 소스 코드.
즉 영속성 컨텍스트 사용방식에 따라 변경할 소스 코드를 작성한다.
package com.springboot.basic;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
// (1) bean 검색 대상 클래스로 지정.
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
// (2) 해당 메서드를 검색 후 리턴 객체를 Spring bean으로 추가하도록 설정
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction();
return args -> {
// (3) 이 곳에 학습할 코드를 타이핑 예정.
};
}
}
[수정할 소스]
3. 영속성 컨텍스트에 엔티티 저장하기
그렇다면 기본적인 것 부터 하나씩 진행해보도록 하자.
우선 영속성 컨텍스트에 엔티티를 저장하기 위해 우리는 [수정할 소스] 에 영속성 컨테스트를 저장하는 example01 메서드를 작성해보자.
// package와 import는 생략...
@Configuration
public class JpaBasicConfig {
private EntityManager em;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) { // (1)
this.em = emFactory.createEntityManager(); // (2)
return args -> {
example01();
};
}
private void example01() {
Member member = new Member("hgd@gmail.com");
em.persist(member); // (3)
Member resultMember = em.find(Member.class, 1L); // (4)
System.out.println("Id: " + resultMember.getMemberId() + ", email: " +
resultMember.getEmail());
}
}
JPA 영속성 컨텍스트는 EntityManager 클래스에 의해서 관리가 된다.
EntityManager 클래스의 객체는 (1)과 같이 EntityManagerFactory 객체를 DI( 의존성 주입)을 받을 수 있다.
(2)에서는 EntityManagerFactory의 craeeteEntityManager()메서드를 통해 EntityManger를 생성 후 객체를 얻을 수 있다.
(3)을 보면 persist(object) 메서드를 호출하여 영속성 컨텍스트에 인자값 객체의 정보들을 저장 할 수 있다.
위 소스에서는 member 객체의 정보들이 저장된다고 보면 된다.
(4)에서는 영속성 컨텍스트에 member 객체가 잘 저장되었는지 확인하는 부분이다.
확인하기 위해서 find(entity.class, entity_식별자값)을 통하여 확인 할 수 있다.
그림으로 간단하게 그리자면 아래와 같다.
em.persist(member)를 호출하게 되면은 member 객체가 1차 캐시에 저장되며, 이 객체는 쓰기 지연 SQL 저장소에 INSERT 쿼리 형태로 등록이 된다.
여기서 알아둬야 할 것은 member 객체를 저장하지만 실제 테이블에서는 INSERT를 하지 않아 회원 정보를 저장하고 있지는 않다는 점이다.
4. 영속성 컨텍스트와 테이블에 엔티티 저장하기
그렇다면은 실제 테이블에 저장하기 위해서는 어떻게 해야할까.
아래와 같이 소스를 작성하면 된다 [수정할 소스]를 변경하도록 하겠다.
// package와 import는 생략...
@Configuration
public class JpaBasicConfig {
private EntityManager em;
private EntityTransaction tx;
@Bean
public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory) {
this.em = emFactory.createEntityManager();
this.tx = em.getTransaction(); // (1)
return args -> {
example02();
};
}
private void example02() {
tx.begin(); // (2)
Member member = new Member("hgd@gmail.com");
em.persist(member); // (3)
tx.commit(); // (4)
Member resultMember1 = em.find(Member.class, 1L); // (5)
System.out.println("Id: " + resultMember1.getMemberId() +
", email: " + resultMember1.getEmail());
Member resultMember2 = em.find(Member.class, 2L); // (6)
System.out.println(resultMember2 == null); // (7)
}
}
(1)과 같이 영속성 컨텍스트 안에 엔티티를 저장하기위해 getTransaction()을 DI해야한다.
(2)와 같이 Transaction을 시작하기 위해서 tx.begin() 메서드를 호출 한다.
(3)은 이전 작업과 같이 member를 영속성 컨텍스트에 저장하는 작업을 하는 것이다.
(4)는 tx.commit()을 호출하여 해당 시점에 영속성 컨텍스트에 저장되어 있는 member 객체를 데이터베이스의 테이블에 저장한다.
(5)에서 em.find()를 호출하여 (3)작업에서 저장했던 member 객체를 1차 캐시에서 조회한다.
(6)에서 2번 ID를 가지고 있는 member객체를 찾는데, 우선 영속성 컨텍스트에 값을 먼저 찾고, 우리는 2L member의 객체가 존재하지 않기 때문에 테이블에 직접 SELECT 쿼리를 전송해서 값을 찾는다.
(7)결국은 값이 없기에 true가 반환된다.
그림으로 반환하면 아래와 같다.
'Spring 지식 및 공부' 카테고리의 다른 글
[Spring] Spring JPA - 앤티티 간 연관 관계 매핑 OneToMany & ManyToOne & OneToOne & ManyToMany (0) | 2024.07.16 |
---|---|
[Spring] Spring JPA - 직렬화 순환 참조 @Jsonbackreference & @Jsonmanagedreference (0) | 2024.07.13 |
[Spring] Spring JPA - 기본개념 (0) | 2024.07.09 |
[Spring] Spring MVC (0) | 2024.07.08 |
[Spring] 자주 사용하는 어노테이션(Annotation) 정리 (0) | 2024.07.06 |