스파르타 코딩 클럽 | 자바 심화 과정/Spring Master (입문 주차)

Entity - #3. JPA 트랜잭션 (데이터 변경 SQL을 DB에 요청 및 반영하기 위해서는 트랜잭션 필요)

luminous_dev 2025. 2. 3. 14:53

트랜잭션이란?

 

트랜잭션

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

: 데이터베이스의 데이터들을 안전하게 관리하기 위해서 생겨난 개념 

 

가장 큰 특징)

여러 개의 SQL하나의 트랜잭션에 포함될 수 있음

→ 모든 SQL이 성공적으로 수행되면 DB에 영구적으로 변경 반영 / 단 하나라도 실패하면 모든 변경 되돌림

 

 

MySQL에서 직접 트랜잭션 사용해보기

mySQL 콘솔에 붙여넣기

START TRANSACTION; # 트랜잭션을 시작합니다.

INSERT INTO memo (id, username, contents) VALUES (1, 'Robbie', 'Robbie Memo');
INSERT INTO memo (id, username, contents) VALUES (2, 'Robbert', 'Robbert Memo');
SELECT * FROM memo;

COMMIT; # 트랜잭션을 커밋합니다.

SELECT * FROM memo;

 

 

하나씩 ctrl + enter해서 실행

 

Insert까지 하면 실제 데이터베이스가 생성되진 않으나 조회는 됨 

Commit하면 트랜잭션이 커밋되고 종료 

 

영속성 컨텍스트에다 Entity 객체를 저장했다고 해서 데이터 베이스에 바로 반영되는 것 x

 

JPA에서도 영속성 컨텍스트로 관리하고 있던 Entity 객체들의 정보를 

쓰기 지연 저장소에 전부 가지고 있다가 커밋이 되면 변경할 요청을 반영함

 

 

데이터베이스에 트랜잭션을 적용하면 효율적임

실수 등 만약의 상황을 대비, 마지막으로 진짜 문제가 없을 때 한 번에 반영하는게 훨씬 더 효율적

 

영속성 컨텍스트 확인하는 테스트 코드

1. Entity Transaction 성공 테스트

 

1) EntityTest.java에 붙여넣기 

@Test
@DisplayName("EntityTransaction 성공 테스트")
void test1() {
    EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.

    try { // DB 작업을 수행합니다.

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setId(1L); // 식별자 값을 넣어줍니다.
        memo.setUsername("Robbie");
        memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
        // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
    } finally {
        em.close(); // 사용한 EntityManager 를 종료합니다.
    }

    emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}

 

처음에 오류가 뜨면 Setter이 없어서 그럼 

 

2) Memo.java 클래스에 @Getter 추가 (alt + insert > getter 및 setter)

주의사항) EntityClass에서 Setter을 이용할 때는 데이터베이스와 맵핑되니까 조심해야 함

package com.sparta.entity;

import jakarta.persistence.*;

@Entity // JPA가 관리할 수 있는 Entity 클래스 지정
@Table(name = "memo") // 매핑할 테이블의 이름을 지정
public class Memo {
    @Id
    // 테스트 때는 빼주기
    // @GeneratedValue(strategy = GenerationType.IDENTITY)
    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;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getContents() {
        return contents;
    }

    public void setContents(String contents) {
        this.contents = contents;
    }
}

 

3) debug 시작 > 아래 페이지 참고 

 

인텔리제이 - Debugging하는 법

1. 빨간색 누르기 2. 우측 버튼 누르기 > All로 된거 Thread로 바꾸고 Make Default 눌러주기  3. 이제 저 play버튼 누르면 디버그 가능  주의사항) 테스트할 때는 직접 id값을 넣어줄 거라서 @GeneratedValue

luminous0115.tistory.com

 

 


EntityTest.java 상세 설명

@Test
@DisplayName("EntityTransaction 성공 테스트")
void test1() {
    EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.아래 설정해둔 내용들이 실행됨

    try { // DB 작업을 수행할 때 발생하는 오류 잡기 위함 

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setId(1L); // 식별자 값을 넣어줍니다.
        memo.setUsername("Robbie");
        memo.setContents("영속성 컨텍스트와 트랜잭션 이해하기");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
        // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback 호출
    } finally {
        em.close(); // 사용한 EntityManager 를 종료
    }

    emf.close(); // 사용한 EntityManagerFactory 를 종료
}

 

  •  em.persist(memo);
    • 영속성 컨텍스트에 저장하고 싶은 Entity Class 객체를 ()안에 넣어주면 영속성 컨텍스트에 담기게 됨
  • debug 콘솔 > em > persistenceContext > Memo#1의 #1 중요함 

 

    • debug 콘솔 > em > persistenceContext > entitiesByKey보면 Memo#1이 저장되어 있음 

 

identifier = PK 
value = Memo Entity가 저장됨 

 

  • 실제로 JPA에서 트랜잭션 사용하기
    • EntityTransaction et = em.getTransaction(); : EntityManager에서 EntityTransaction 가지고 올 수 있음  
    • et.begin(); // 트랜잭션 시작 - 아래 설정해둔 환경이 실행됨

2. Entity Transaction 실패 테스트

@Test
@DisplayName("EntityTransaction 실패 테스트")
void test2() {
    EntityTransaction et = em.getTransaction(); // EntityManager 에서 EntityTransaction 을 가져옵니다.

    et.begin(); // 트랜잭션을 시작합니다.

    try { // DB 작업을 수행합니다.

        Memo memo = new Memo(); // 저장할 Entity 객체를 생성합니다.
        memo.setUsername("Robbert");
        memo.setContents("실패 케이스");

        em.persist(memo); // EntityManager 사용하여 memo 객체를 영속성 컨텍스트에 저장합니다.

        et.commit(); // 오류가 발생하지 않고 정상적으로 수행되었다면 commit 을 호출합니다.
        // commit 이 호출되면서 DB 에 수행한 DB 작업들이 반영됩니다.
    } catch (Exception ex) {
        System.out.println("식별자 값을 넣어주지 않아 오류가 발생했습니다.");
        ex.printStackTrace();
        et.rollback(); // DB 작업 중 오류 발생 시 rollback 을 호출합니다.
    } finally {
        em.close(); // 사용한 EntityManager 를 종료합니다.
    }

    emf.close(); // 사용한 EntityManagerFactory 를 종료합니다.
}

ㄴ Auto Increment를 빼놓았기 때문에 식별자 값을 필수로 지정해야 한다는 오류가 발생