
JPA(Java Persistence API)를 사용하여 Entity를 저장할 때, JPA에서는 persist() 메소드를 사용합니다. persist() 메소드는 Entity를 영구 저장소에 저장하는 작업을 의미합니다. 이 과정에서 INSERT 쿼리가 실행됩니다. 여기서 주목할 점은 persist() 메소드가 호출되었을 때, 즉시 INSERT 쿼리가 실행되는 것이 아니라는 것입니다.
대신, JPA는 Entity를 영속화(persist)하고, 해당 Entity의 변경사항을 추적합니다. 이 변경사항은 일시적으로 쓰기 지연 SQL 저장소에 보관됩니다. 이 저장소는 변경사항들이 즉시 데이터베이스에 반영되지 않고, 트랜잭션이 커밋될 때까지 기다리는 곳입니다.
트랜잭션이 커밋될 때, JPA는 쓰기 지연 SQL 저장소에 저장된 INSERT 쿼리를 실행하여 데이터베이스에 영구적인 변경을 적용합니다. 이를 통해 트랜잭션 단위로 모든 변경사항이 원자적으로 처리되고, 데이터 일관성을 보장할 수 있습니다. 따라서 persist() 메소드는 단순히 INSERT 쿼리를 실행하는 것이 아니라, 영속화되는 Entity의 변경사항을 추적하여 트랜잭션이 커밋될 때 실행될 수 있도록 준비합니다.
이 방식은 성능을 향상시키고, 데이터베이스 작업을 최적화할 수 있도록 해줍니다.
auto increment를 사용하게 되면 데이터베이스에서 기본 키 값을 관리하게 됩니다. 그리고 JPA는 트랜잭션 커밋 시점에 쓰기 지연 SQL 저장소에 저장된 쿼리를 실행하여 데이터베이스에 변경사항을 반영합니다.
그러나 한 트랜잭션 안에서 em.persist(member)를 한 이후에 em.find(Member.class, member.getId())를 호출하면, 데이터베이스에는 아직 해당 데이터가 존재하지 않기 때문에 데이터를 조회할 수 없는 문제가 발생하게 됩니다.
JPA는 이런 문제를 해결하기 위해 해당 Entity가 auto increment를 사용한 경우, em.persist()를 호출하는 시점에 즉시 데이터베이스에 쿼리를 실행하여 데이터를 저장합니다. 따라서 데이터가 즉시 데이터베이스에 저장되므로 em.find()를 호출해도 데이터를 조회할 수 있게 됩니다. 이렇게 함으로써 즉각적인 데이터 접근을 가능하게 합니다.
member_id 필드가 auto increment를 사용하는 방식으로 생성되는 예시를 살펴보겠습니다.
위에서 설명한 내용을 테스트 코드로 확인해보겠습니다.
auto increment를 사용하지 않는 경우와 사용하는 경우의 차이점을 확인할 수 있습니다.
Member Entity
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Member {
@Id
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String username;
}
테스트 코드
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
@Transactional
@SpringBootTest
class JpaAutoIncrementTest {
@Autowired
private EntityManager em;
@Test
void test() {
Member member = new Member();
member.setId(1L);
member.setUsername("hello");
System.out.println("========비영속=======");
em.persist(member);
System.out.println("========영속=======");
tx.commit();
System.out.println("========커밋 후========");
}
}
위는 auto increment를 사용하지 않는 경우의 테스트 코드입니다.
테스트 결과
========비영속=======
========영속=======
Hibernate:
/* insert for
hellojpa.Member */insert
into
Member (username, member_id)
values
(?, ?)
========커밋 후========
persist() 메소드를 호출한 시점에 INSERT 쿼리가 실행되지 않고, 트랜잭션이 커밋될 때 실행됨을 알 수 있습니다.
Member Entity
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "member_id")
private Long id;
@Column(name = "username")
private String username;
}
auto increment를 사용하는 경우, @GeneratedValue(strategy = GenerationType.IDENTITY)를 사용하여 데이터베이스에서 기본 키 값을 관리하도록 설정합니다.
테스트 코드
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.beans.factory.annotation.Autowired;
import jakarta.persistence.EntityManager;
import org.junit.jupiter.api.Test;
@Transactional
@SpringBootTest
class JpaAutoIncrementTest {
@Autowired
private EntityManager em;
@Test
void test() {
Member member = new Member();
// member.setId(1L);
member.setUsername("hello");
System.out.println("========비영속=======");
em.persist(member);
System.out.println("member.getId() = " + member.getId());
System.out.println("========영속=======");
tx.commit();
System.out.println("========커밋 후========");
}
}
위는 auto increment를 사용하는 경우의 테스트 코드입니다.
테스트 결과
========비영속=======
Hibernate:
/* insert for
hellojpa.Member */insert
into
Member (username, member_id)
values
(?, default)
member.getId() = 1
========영속=======
========커밋 후========
auto increment를 사용한 경우, persist() 메소드를 호출한 시점에 즉시 데이터베이스에 INSERT 쿼리가 실행되어 데이터가 저장됨을 알 수 있습니다.
또한, member에 id값을 member.setId() 해준 적이 없는데, member.getId()를 호출하면 1이라는 값이 출력됨을 알 수 있습니다. 이는 데이터베이스에서 자동으로 생성된 기본 키 값을 JPA가 가져와서 member 객체에 저장했기 때문입니다.
persist() 메소드를 호출한 시점에 즉시 데이터베이스에 INSERT 쿼리가 실행되지 않고, 쿼리는 쓰기 지연 SQL 저장소에 저장되었다가 트랜잭션이 커밋될 때 실행됩니다.persist() 메소드를 호출한 시점에 즉시 데이터베이스에 INSERT 쿼리가 실행됩니다.