thumbnail

이미 한번의 면접에서


"Spring 트랜잭션이 어떻게 동작하는지 아시나요?" 라는 질문을 받았습니다.


답변을 못하였으니, 공부를 해야합니다.


답변은 "Spring은 트랜잭션은 AOP Proxy 통해 처리됩니다. " 입니다


트랜잭션 설정 전에는 메소드 호출에, 다음과 같이 동작합니다.


pasteImage.png

@Service
public class AccountServiceImpl  implements AccountService {
  //…
<br/>
  //Not specifying a transaction policy here!
  public void create(Account account) {
  entityManager.persist(account);
  }
}



하지만 트랜잭션을 설정하였다면, 다음과 같이 프록시 클래스가 Impl 클래스를 감싸게 됩니다.


pasteImage.png


그리고 다음과 같이 동작합니다.

  • Caller에서 proxy로 create메소드 호출
  • proxy에서 transaction 시작( autoCommit(false) )
  • proxy에서 accountServiceImpl로 create 메소드 호출
  • proxy에서 transaction 후 처리 ( commit 또는 rollback)




지금 까지의 내용은  JDK Dynamic Proxy 경우 입니다.


Spring은  JDK Dynamic Proxy, CGLIB 두 가지 프록시 타입을 사용합니다.


인터페이스를 구현한 타겟에서는  JDK Dynamic Proxy를, 인터페이스를 구현하지 않은 타겟은 CGLIB를 사용합니다.


설정을 통해 모두 CGLIB 프록시를 사용 할 수도 있습니다.


CGLIB에 따라 약간의 처리가 달라집니다. 다음 그림은 CGLIB에 따른 Proxy 동작 과정입니다.


인터페이스가 없기때문에, 타겟 클래스를 상속받아 Proxy를 정의하고, super 메소드를 호출함으로써 핵심기능을 수행합니다.


pasteImage.png



그럼 Spring이 AOP Proxy를 통해서 동작하는 과정을 이해하였으니, 어떻게 구현해야하는지 알아야합니다. 매우 간단합니다.







XML 설정 파일에서 다음과 같이 설정합니다.

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSourceSpied"  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass  data-tomark-pass />
	</bean>
	<tx:annotation-driven /></code>



그리고 트랜잭션이 필요한 메소드 위에  @Transactional 어노테이션을 추가하면 됩니다.

@Transactional
	public BlogVo doView(Set<Integer> isVisitBlogs, int seq) {
	      //...
	}



또한 클래스 위에 @Transactional을 설정한다면 해당 클래스의 모든 메소드에 대해서 적용됩니다.

        @Transactional
	public  class A{
	      //...
	}



MVC구조에 의하여, @Transactional 어노테이션은 Service 단에서 추가하는 것이 권장됩니다.
트랜잭션은 여러 CRUD 작업을 하나의 동작으로 하기 위함이고, 비즈니스 로직을 처리하는 Service단에서 트랜잭션을 수행하는 것이 권장됩니다.


"이상적으로, 서비스 계층 (Manager)은 비즈니스 로직을 나타내므로 @Transactional로 주석 처리되어야합니다.서비스 계층은 다른 DAO를 호출하여 DB 작업을 수행 할 수 있습니다. 서비스 메소드에 3 가지 DAO 연산이있는 상황을 가정합니다. 첫 번째 DAO 작업이 실패하면 다른 두 작업이 계속 전달 될 수 있으며 일관성없는 DB 상태가 종료됩니다. Annotating Service 레이어는 이러한 상황에서 당신을 구할 수 있습니다."


출처 : https://stackoverflow.com/questions/3886909/where-should-transactional-be-place-service-layer-or-dao




@Transactional 어노테이션에 여러 옵션값들을 가지고 있습니다.






isolation (격리레벨)
앞서 공부한, 여러 트랜잭션 동시처리로 인한 문제 발생에, 트랜잭션별 격리레벨을 다음과 같이 설정 할 수 있습니다.


설정 예: @Transactional(isolation=Isolation.DEFAULT)

  • DEFAULT: DB 설정, 기본 격리 수준(기본설정)
  • SERIALIZABLE : 가장 높은 격리, 성능 저하의 우려가 있음
  • READ_UNCOMMITED : 커밋되지 않는 데이터에 대한 읽기를 허용
  • READ_COMMITED : 커밋된 데이터에 대해 읽기 허용
  • REPEATEABLE_READ : 동일 필드에 대해 다중 접근 시 모두 동일한 결과를 보장


    MySQL에서 트랜잭션 격리레벨은 다음 SQL문을 통해 확인 할 수 있습니다.

SHOW VARIABLES LIKE 'tx_isolation';






propagation (전파속성)
트랜잭션 전파속성에 대한 처리입니다.


예를 들어 현재 트랙잭션이 설정되어있는 A메소드가 호출 되었을때,
A메소드를 호출한 B메소드가 이미 트랜잭션을 가지고 있으면 어떻게 처리 할 것인가 대한 옵션값입니다.


설정 예: @Transactional(propagation=Propagation.REQUIRED)

  • PROPAGATION_MANDATORY : 작업은 반드시 특정한 트랜잭션이 존재한 상태에서만 가능
  • PROPAGATION_NESTED : 기존에 트랜잭션이 있는 경우, 포함되어서 실행
  • PROPAGATION_NEVER : 트랜잭션 상황에 실행되면 예외 발생
  • PROPAGATION_NOT_SUPPORTED : 트랜잭션이 있는 경우에는 트랜잭션이 끝날 때까지 보류된 후 실행
  • PROPAGATION_REQUIRED : 트랜젝션이 있으면 그 상황에서 실행, 없으면 새로운 트랜잭션 실행(기본설정)
  • PROPAGATION_REQUIRED_NEW : 대상은 자신만의 고유한 트랜잭션으로 실행
  • PROPAGATION_SUPPORTS : 트랜젝션을 필요료 하지 않으나, 트랜잭션 상황에 있다면 포함되어서 실행


    ! 우아한형제들 블로그에, 전파속성과 관련된 재밌는 글이 있습니다.


    http://woowabros.github.io/experience/2019/01/29/exception-in-transaction.html



    readOnly 속성


    설정 예: @Transactional(readOnly = true)


    true인 경우 insert, update, delete 실행 시 예외 발생, 기본 설정은 false



    rollbackFor 속성


    트랜잭션의 롤백은 Error, Unchecked Exception에 대해서만 롤백 처리됩니다.
    따라서 CheckedException에 대해서 롤백을 수행할려면, rollbackFor 옵션을 이용해야 합니다.
    설정 예: @Transactional(rollbackFor=Exception.class)

  • 특정 예외가 발생 시 강제로 Rollback





    noRollbackFor 속성


    설정 예: @Transactional(noRollbackFor=Exception.class)

  • 특정 예외의 발생 시 Rollback 처리되지 않음





    timeout 속성


    설정 예: @Transactional(timeout=10)

  • 지정한 시간 내에 해당 메소드 수행이 완료되이 않은 경우 rollback 수행. -1일 경우 no timeout(Default=-1)

참고



https://spring.io/blog/2012/05/23/transactions-caching-and-aop-understanding-proxy-usage-in-spring


http://egloos.zum.com/aretias/v/708477
https://mozi.tistory.com/201

CommentCount 0
이전 댓글 보기
등록
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.
TOP