반응형

서비스 함수에서 jpa repository에 정의된 쿼리 함수를 호출하는데에서 위와 같은 "No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call" 오류가 발생하였다.

 

오류가 발생한 코드의 위치

remove 명령을 실행하는 과정에서 트랜잭션과 관련된 문제가 발생한 것을 확인할 수 있다.

 

알아보니 이 오류는 스프링 트랜잭션 관련 문제로, 현재 스레드에 실제 트랜잭션이 없기 때문에 'remove' 호출을 신뢰할 수 없다는 것을 의미한 것이었다.

 

위 오류는 다음 두 가지 상황에서 발생할 수 있는데,

 

첫번째, 스프링의 트랜잭션 관리 설정이 올바르게 구성되어 있는지?

예를 들어, @EnableTransactionManagement 어노테이션이 설정되어 있는지 확인하고, 트랜잭션 관리자가 적절히 설정되어 있는지 확인해야 한다!

 

두번째, 트랜잭션 어노테이션이 메서드나 클래스에 적용되지 않은 경우

@Transactional 어노테이션을 메서드나 클래스에 추가하여 트랜잭션을 활성화해서 해결할 수 있다!

 

나의 경우 두번째에 해당!!!!

 

나의 코드를 보면, 트랜잭션 경계를 벗어난 상황에서 레포지토리 함수를 호출하고 있다. 

스프링에서 트랜잭션은 일반적으로 서비스 레이어에서 관리되는데, 다른 레이어(컨트롤러)에서 트랜잭션 경계를 벗어나서 호출하면 이러한 오류가 발생할 수 있다.

따라서 트랜잭션 범위 내에서 해당 호출을 수행하도록 코드를 구성해야 한다!

 

아래와 같이 해결하였다.

 

서비스 레이어에서 트랜잭션을 관리하도록 어노테이션 추가

 

 

이전에 비슷한 오류가 발생하였을 때, 레포지토리 함수 자체에 @transactional 어노테이션을 추가하여 해결한 적이 있다.

 

그렇다면, 레포지 함수 자체에 어노테이션을 추가하는 것과, 이를 호출하는 서비스 함수에 어노테이션을 추가하는 것 중 어느것이 더 적절한 방법일까?

 

일반적으로 서비스 계층에서 @Transactional 어노테이션을 추가하는 것이 더 좋은 설계이다!!!

 

이유는 다음과 같다

 

  1. 레이어 간의 역할 분리 : 서비스 계층은 비즈니스 로직을 처리하는 데 책임이 있으며, 트랜잭션 관리 역시 서비스 계층에서 이루어져야 한다. 따라서 트랜잭션 관리는 서비스 계층의 역할이며, 레포지토리는 데이터 액세스에 집중해야 한다. 이를 위해 트랜잭션 관리는 서비스 계층에 위임하는 것이 좋다!
  2. 트랜잭션 경계의 명확성: 서비스 계층에서 @Transactional 어노테이션을 추가하면 트랜잭션의 범위가 서비스 메서드의 호출과 일치하게 된다. 이는 트랜잭션 경계를 명확하게 설정하여 예기치 않은 동작을 방지하는 데 도움이 된다.
  3. 트랜잭션 관리의 유연성: 서비스 계층에서 트랜잭션 관리를 담당하면 여러 레포지토리 메서드를 호출하는 복잡한 비즈니스 로직에서도 단일 트랜잭션으로 묶을 수 있다. 이는 일관된 데이터 처리와 롤백을 보장하여 데이터의 무결성을 유지하는 데 도움이 된다.

따라서, 가능하다면 레포지토리에서 직접 @Transactional 어노테이션을 추가하는 대신 해당 함수를 호출하는 서비스 계층에서 @Transactional 어노테이션을 추가하는 것이 좋다.

 

이렇게 하면 역할과 책임이 분리되며, 트랜잭션 관리가 명확하고 유연하게 이루어질 수 있다!!!

 

"트랜잭션 관리를 명확히 하기!!!"

반응형
반응형

병행제어(concurrency control)를 하지 않을 때 발생할 수 있는 문제점

1) 갱신 분실 (lost update)

- 두 트랜잭션이 동시에 같은 데이터를 갱신할 때, 하나의 트랜잭션이 그 데이터를 갱신하기 전에 다른 트랜잭션이 갱신함으로써 한 트랜잭션의 갱신 연산이 무효화됨.

 

2) 모순성 (inconsistency)

- 하나의 트랜잭션이 여러 데이터 갱신 연산을 수행할 때, 일관성 없는 상태의 데이터베이스에서 데이터를 가져옴으로써 데이터의 불일치가 발생하는 것을 의미한다. , 트랜잭션에서 이루어지는 연산대로 전과 후가 일관성이 있어야 하는데, 중간에 다른 트랜잭션의 연산작업으로 인해 일관성이 없게 됨.

 

3) 연쇄 복귀 (cascading rollback)

- 병행 수행되던 둘 이상의 트랜잭션 중 어느 한 트랜잭션에 오류가 발생하여 rollback하면 다른 트랜잭션들도 함께 rollback되는 현상.

그런데, 복수의 트랜잭션이 데이터 공유 시 특정 트랜잭션이 처리를 취소할 경우 다른 트랜잭션이 처리한 부분에 대해 취소 불가능

⇒ rollback을 하고자 하는 트랜잭션이 처음상태로 돌아가려고 해도 다른 트랜잭션에서 이미 연산 작업을 완료하고 떠났기 때문에 복귀 작업 불가능

 

반응형

'Database' 카테고리의 다른 글

트랜잭션 격리 수준(Isolation level) 개념 정리  (0) 2022.12.27
반응형

트랜잭션 isolation level

 

트랜잭션 isloation level은 아래의 4가지 단계로 구분된다.

    • read uncommited: commit되지 않은 데이터도 읽을 수 있다
    • read committed: commit된 데이터만 읽는다
    • repeatable read: 하나의 트랜잭션에서는 하나의 스냅샷만 사용
    • serializable: read시에 DML작업이 동시에 진행될 수 없다

 

transaction isolation level table

- 따라서 read commited의 경우 하나의 트랜잭션에서 같은 쿼리를 두 번 실행하면 처음의 쿼리결과와 두번째 쿼리 결과가 다를 수 있다.

- repeatable read는 하나의 트랜잭션에서 같은 쿼리를 두 번 실행하더라도 두 결과가 동일하다.

 

여기서 Phantom read란?

Phantom read : 한 트랜잭션 내에서 같은 쿼리를 두 번 수행 시, 첫 번째 쿼리에서 없던 레코드(유령, Phantom)가 두 번째 쿼리에서 발생하는 현상

 

non-repeatable read와 phantom read는 무슨 차이??

둘 다 한 트랙잭션 내에서 같은 쿼리를 두 번 실행했을 때 처음 쿼리 결과와 두번째 쿼리 결과가 다를 때를 말한다.

- non-repeatable read는 한 레코드의 값이 다른 경우, 즉 달라진 value에 초점

- phantom read는 없던 레코드가 생기거나, 있던 레코드가 없어지는 경우, 즉 레코드의 존재 유무에 초점

 

repeatable read isolation level에서는 non-repeatable read가 발생하지 않는다.

- 다른 트랜잭션이 수정사항을 commit하더라도, 그 트랜잭션이 자신보다 늦게 생긴 트랜잭션이면 해당 변경사항을 반영하지 않기 때문이다.

반응형

+ Recent posts