JPA

[JPA] no entitymanager with actual transaction available for current thread

Empty Brain 2023. 1. 4. 17:15

나다.

 

 

이 글이 뭐하는 글이냐면

우리는 살면서 어이없는 일들과 마주하게 된다.

그리고 대부분의 어이없는 일들은 사소한 실수에서 비롯된다.

 

이 글의 제목 또한 굉장히 사소한 실수에서 발생하는 개 같은 일인데

이번 글을 통해 같은 실수를 하는 사람이 적어지길 바라는 마음으로 쓴다.

 


왜 이 오류가 발생했냐면

우리는 어떤 코드를 완성하면 테스트를 한다.

이는 척수반사와 같은 현상인데 테스트는 무조건적으로 시행되어야만

코드에 존재하는 하자를 검증할 수 있기 때문이다.

 

물론 비단 테스트를 할 때뿐 아니라 어느 경우든 발생할 수 있지만

내 생각에 대부분 테스트 단계에서 어떤 것을 저장할 때 발생하게 될 텐데

 

이 오류는 EntityManager가 persist() 함수를 호출 받았으나

함수를 처리 할 Transaction 단위가 존재하지 않을 때 출력되는 오류다.

 

맞음. @Transactional 안 붙혀서 출력되는 오류임.

 

다음의 테스트는 정상작동하지 않는다.

X발.

 

하지만 위의 테스트에 @Transactional을 붙여준다면

거울

 

잘 된다.

 

JPA는 Transaction 단위로 기능을 수행한다.Transaction 단위로 구성된 작업에 따라 1차 캐시에 저장한 entity 들이 데이터베이스에 flush되어 영속화되는 구조인데 Transaction 자체가 없으니동작할 리가 없지 않은가?

 


편하게 사용하고 싶다면

@Transactional을 모든 메소드마다 붙히고 다닌다면 개같이 귀찮아질 것이다.

그렇기 때문에 이 Transactional 어노테이션은 클래스 단위로도 붙일 수 있는데

 

기본적으로 @Transactional의 속성 중 readonly의 경우 default가 false로 설정되어 있다.

 

맞음. true면 읽기 전용임 ㅇㅇ.

 

그렇기 때문에 경우에 따라 사용법을 좀 바꾸면 편하게 쓸 수 있는데

해당 클래스가 수행해야 할 항목 중 조회의 성격이 많은 경우

@Service
@RequiredArgsConstructor
@Transactional(readonly = true)
class 어쩌고저쩌고Service {

    private final 어쩌고Repository 저쩌고Repository

    void 읽는거(Long 읽어야 할거 아이디) {
    	블라블라;
    }
    
    @Transactional
    void 쓰는거(저장해야 될 객체) {
    	블라블라;
    }
}

클래스에 통째로 readonly를 씌워놓은 후 읽기가 아닌 쓰기 등과 같은 동작을 해야 할 메소드에만

따로 @Transactional 을 붙여 사용하면 된다.

클래스보다 메소드에 붙어있는게 우선으로 적용됨.

@Service
@RequiredArgsConstructor
@Transactional
class 어쩌고저쩌고Service {

    private final 어쩌고Repository 저쩌고Repository;

    @Transactional(readonly = true)
    void 읽는거(Long 읽어야 할거 아이디) {
    	블라블라;
    }
    
    void 쓰는거(저장해야 될 객체) {
    	블라블라;
    }
}

반대로 쓰면 이렇게 되겠쥬?

 

그러니 저 오류를 본다면 민첩하게 @Transactional을 붙이도록 하자.

그럼 다들 민첩한 하루 되시길 :)

'JPA' 카테고리의 다른 글

[JPA] 연관관계 맵핑(다대일 / N:1)  (2) 2022.12.28
[JPA] JPA와 PK 그리고 Id  (1) 2022.12.28
[JPA] 영속성 컨텍스트와 Flush  (4) 2022.12.22
[JPA] 영속성 컨텍스트란 무엇인가.  (0) 2022.12.22
[JPA] JPA란 무엇인가.  (1) 2022.12.20