본문 바로가기

코딩/미완

@Transactional 주의점. 영속성 컨텍스트의 생명주기

아래처럼 @Transactional을 붙인 상태에서는 setFcmToken()에 따른 update문이 동작한다.

@SpringBootTest
@Commit
@Transactional

class CustomerRepositoryTest {

    @Autowired
    CustomerRepository customerRepository;

    /**
     * 없을 때 null을 반환하지는 않는다. 빈 리스트를 반환함.
     */
    @Test
    public void findBySnsTypeAndSnsIdentifyKeyTest() {
        Customer customer = Customer.builder().build();
        customerRepository.save(customer);
        customer.setFcmToken("1");
        
        List<Customer> li = customerRepository.findBySnsTypeAndSnsIdentifyKey(SNSType.NAVER, "1");//flush호출?
        
        
        System.out.println(li);
        if (li == null) {
            System.out.println("null");

        } else {
            System.out.println("not null");
        }
    }
}

////아래는 결과!
Hibernate: 
    insert 
    into
        customer
        (created, modified, birth_year, email, fcm_token, gender, name, phone_num, sns_identify_key, sns_type, uuid) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIMESTAMP] - [2022-08-12T17:54:10.816035]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [TIMESTAMP] - [2022-08-12T17:54:10.816035]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [INTEGER] - [0]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [6] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [7] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [9] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [10] as [VARCHAR] - [null]
2022-08-12 17:54:10.894 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [11] as [BINARY] - [7addddde-bb14-477b-bb66-9ded01d5a806]
Hibernate: 
    update
        customer 
    set
        modified=?,
        birth_year=?,
        email=?,
        fcm_token=?,
        gender=?,
        name=?,
        phone_num=?,
        sns_identify_key=?,
        sns_type=? 
    where
        uuid=?
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIMESTAMP] - [2022-08-12T17:54:10.878538200]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [INTEGER] - [0]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [VARCHAR] - [1]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [6] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [7] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [9] as [VARCHAR] - [null]
2022-08-12 17:54:10.909 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [10] as [BINARY] - [7addddde-bb14-477b-bb66-9ded01d5a806]
Hibernate: 
    select
        customer0_.uuid as uuid1_1_,
        customer0_.created as created2_1_,
        customer0_.modified as modified3_1_,
        customer0_.birth_year as birth_ye4_1_,
        customer0_.email as email5_1_,
        customer0_.fcm_token as fcm_toke6_1_,
        customer0_.gender as gender7_1_,
        customer0_.name as name8_1_,
        customer0_.phone_num as phone_nu9_1_,
        customer0_.sns_identify_key as sns_ide10_1_,
        customer0_.sns_type as sns_typ11_1_ 
    from
        customer customer0_ 
    where
        customer0_.sns_type=? 
        and customer0_.sns_identify_key=?
2022-08-12 17:54:10.925 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [NAVER]
2022-08-12 17:54:10.925 TRACE 12676 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [1]
[]
not null

DB에도 잘 반영된 것을 알 수 있다.

 


@Commit을 떼고 실행해보았다. SQL은 똑같이 실행되지만 DB에서 확인할 수는 없었다. 테스트 환경에서는 @Transactional이 있으면 자동 롤백이다.


@Transactional, @Commit 둘 다 빼고 실행해보았다.

row는 하나가 생겼지만 fcm_token값은 들어오지 않았다.(sql을 확인해보면 update문은 실행되지 않았다.)

//실행 결과
Hibernate: 
    insert 
    into
        customer
        (created, modified, birth_year, email, fcm_token, gender, name, phone_num, sns_identify_key, sns_type, uuid) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [TIMESTAMP] - [2022-08-12T18:06:37.127444300]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [TIMESTAMP] - [2022-08-12T18:06:37.127444300]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [3] as [INTEGER] - [0]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [4] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [5] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [6] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [7] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [8] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [9] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [10] as [VARCHAR] - [null]
2022-08-12 18:06:37.174 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [11] as [BINARY] - [9144b42a-0b06-4ae7-9e34-cbe1c9e0a33f]
9144b42a-0b06-4ae7-9e34-cbe1c9e0a33f
Hibernate: 
    select
        customer0_.uuid as uuid1_1_,
        customer0_.created as created2_1_,
        customer0_.modified as modified3_1_,
        customer0_.birth_year as birth_ye4_1_,
        customer0_.email as email5_1_,
        customer0_.fcm_token as fcm_toke6_1_,
        customer0_.gender as gender7_1_,
        customer0_.name as name8_1_,
        customer0_.phone_num as phone_nu9_1_,
        customer0_.sns_identify_key as sns_ide10_1_,
        customer0_.sns_type as sns_typ11_1_ 
    from
        customer customer0_ 
    where
        customer0_.sns_type=? 
        and customer0_.sns_identify_key=?
2022-08-12 18:06:37.252 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [1] as [VARCHAR] - [NAVER]
2022-08-12 18:06:37.252 TRACE 17200 --- [           main] o.h.type.descriptor.sql.BasicBinder      : binding parameter [2] as [VARCHAR] - [1]
[]
not null

<정리>

  1. @Transactional이 없다면 @Commit이 없어도 DB에 반영된다. @Transactional이 있으면 테스트 환경에서는 자동 롤백되기 때문에 @Commit을 붙여줘야한다. 
  2. insert해야 얻을 수 있는 자동생성 uuid나 Auto Increment값은 @Commit하지 않아도 알 수 있다. 롤백은 반영했다가 지우는 것임.(따로 테스트 해보았다.) @Commit해야 알 수 있는 것 아니다.
  3. 테스트 코드에 @Transcational이 없으면 fcm_token의 수정이 반영되지 않았다. setFcmToken()에 따른 update문이 실행안됨.

 

3번을 살펴보자.

왜일까? @Transactional과 영속성컨텍스트의 생명주기와 관련이 있다.

  1. 영속성 컨텍스트는 @Transactional이 begin() 할 때 시작되고 commit() 하면 종료된다. (김영한 JPA 13장에서 확인할 수 있다.)
  2. 같은 @Transactional이라면 같은 영속성 컨텍스트에 접근한다.

두 가지 사실을 종합해보면 테스트 코드에서 @Transactional이 없다면 save한 이후에 영속성 컨텍스트는 종료된다. 그래서 setFcmToken()을 호출해도 영속성 컨텍스트가 종료되었기 때문에 update문이 나가지 않은 것이다.