본문 바로가기

자바/스프링, 스프링부트

JPA 영속성 컨텍스트

영속성 컨텍스트란 엔티티를 영구 저장하는 환경이다.

엔티티를 메모리에서 관리하는 일종의 캐시, JPA는 이 영속성 컨텍스트를 통해 엔티티를 관리하고 수정한다.

 

실제 spring boot 프로젝트 중 "채용 게시판"기능을 JPA로 구현했는데, 실제 코드를 통해 정리했다.

 

 

@Entity // 이 클래스는 JPA 엔터티. 데이터베이스 테이블과 매핑
@Getter
@Setter
public class RecruitmentPostEntity {
	@Id // 기본 키
	@GeneratedValue(strategy = GenerationType.IDENTITY) // 기본키 생성 전략. IDENTITY는 자동 증가
	private Long id;

	private String title;

	@Column(columnDefinition = "TEXT") // 텍스트타입
	private String content;

	@Enumerated(EnumType.STRING) // enum 값을 문자열로 저장 
	private PostStatus status;

	private LocalDate startDate;
	private LocalDate endDate;

	private String createdBy;

	@CreatedDate // 엔터티가 생성될 때 자동으로 현재시간 저장
	private LocalDateTime createdAt;

}

 

 

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
/*
 * 클래스 레벨의 readOnly=true 는 기본적으로 모든 메소드가 읽기 전용임을 설정, 쓰기가 필요한 특정 메소드에서만
 * 별도로 @Transactional을 설정하여 오버라이드하는 패턴.(DB가 리소스를 더 효율적으로 사용)
 */
public class RecruitmentPostService {

	private final RecruitmentPostRepository recruitmentPostRepository;

	public List<RecruitmentPostEntity> getAllPosts() {
		return recruitmentPostRepository.findAll();
	}

	@Transactional // 데이터를 변경하는 작업. (삽입)
	public RecruitmentPostEntity createPost(RecruitmentPostEntity post) {
		return recruitmentPostRepository.save(post);
	}

	@Transactional
	public RecruitmentPostEntity updatePost(Integer id, RecruitmentPostEntity updatedPost) {
		RecruitmentPostEntity post = getPost(id);
		post.setTitle(updatedPost.getTitle());
		post.setContent(updatedPost.getContent());
		post.setStatus(updatedPost.getStatus());
		post.setStartDate(updatedPost.getStartDate());
		post.setEndDate(updatedPost.getEndDate());
		return post;
	}

	@Transactional
	public void deletePost(Integer id) {
		recruitmentPostRepository.deleteById(id);
	}

}

 

 

위와 같은 Entity구조와 Service로직이 있다고 가정한다면,

 RecruitmentPostEntity post = getPost(id);

이 부분에서 영속성 컨텍스트에 엔티티를 로드한다.

 

그리고,

return post;

트랜잭션을 종료하며 자동 저장한다.

 

 

 

 

영속성 컨텍스트의 주요 특징

1. 1차 캐시

영속성 컨텍스트는 1차 캐시를 제공한다. 동일한 엔티티를 조회할 때 데이터베이스 대신 메모리에서 가져온다.

// 최초 조회: DB에서 조회
Post post1 = repository.findById(1).get();

// 두 번째 조회: 1차 캐시에서 조회 (DB 조회 없음)
Post post2 = repository.findById(1).get();

 

2. 변경 감지(Dirty Checking)

영속성 컨텍스트는 엔티티의 변경을 자동으로 감지하여 데이터베이스에 반영한다. 위의 updatePost() 메소드에서 별도의 save() 호출이 없는 것이 바로 이것 때문이다.

@Transactional // 트랜잭션 시작
public void updateTitle(Integer id, String newTitle) {
    RecruitmentPostEntity post = getPost(id);  // 영속성 컨텍스트에 로드
    post.setTitle(newTitle);                   // 변경 감지 동작
    // repository.save(post) 불필요             // 트랜잭션 종료 시 자동 저장
}

 

3. 지연 쓰기(Write Behind)

변경된 내용을 바로 데이터베이스에 반영하지 않고, 트랜잭션이 커밋될 때 모아서 반영한다. 이는 데이터베이스 접근을 최적화하는 역할을 한다.

 

'자바 > 스프링, 스프링부트' 카테고리의 다른 글

필드 주입 vs 생성자 주입  (0) 2025.01.23
@PathVariable, @RequestParam  (0) 2024.12.05
@RequestBody, @ResponseBody  (0) 2024.12.03
Controller  (0) 2024.12.02
스테레오타입 어노테이션(Stereotype Annotations)  (0) 2024.11.29