본문 바로가기
공부/프로그래밍

[spring] @Transactional 작동 안할때 확인해봐야 할 것

by demonic_ 2019. 8. 16.
반응형

인터넷에 나와있는 각종 설정을 해봐도 안될경우 다음을 참조하면서 점검해보자

 

 

1. 메서드가 public 인지 확인

- private 면 걸리지 않는다

public class TranService() {
	@Transactional
	private void test1() { // public 으로 변경
		...
	}	
}

 

 

2. 한 클래스 내 @Transactional 이 설정되어 있지 않은 메서드에서 @Transactional 이 설정된 메서드를 호출할 경우.

 

코드 예)

public class TranService() {
	
	public void test1() {
		test2();
	}

	@Transactional
	public void test2() {
		...
	}	
}

 

수정하려면 두가지 방법이 있다
1) 트랜잭션을 호출하는 메서드에 @Transactional 설정(하위는 제거해도 무방)
2) Class 또는 Bean 을 분리

 

예1)

public class TranService() {

	@Transactional	
	public void test1() {
		test2();
	}

	public void test2() {
		...
	}	
}

 

예2)

public class TranService() {
	@Autowired
	private TranTestService tranTestService;

	public void test1() {
		tranTestService.test2();
	}
}


public class TranTestService() {
	@Transactional
	public void test2() {
		...
	}
}

 

 

2번과 같은 상황이 발생하는 이유

일부로 Exception을 내본 후 트랜잭션 상태를 살펴보면 다음과 같은 에러가 발생해 있다.
(IntelliJ 에서 디버그 모드로 실행)

TransactionAspectSupport.currentTransactionStatus() = NoTransactionException ...

그래서 로그를 살펴보면 다음과 같은 에러가 발생해 있다.

org.springframework.transaction.NoTransactionException: No transaction aspect-managed TransactionStatus in scope
	at org.springframework.transaction.interceptor.TransactionAspectSupport.currentTransactionStatus(TransactionAspectSupport.java:122)

@Transactional 는 Proxy 기반이고 AOP로 구성되어 있다. 이는 Method 혹은 Class가 실행되기 전/후 등의 단계에서 자동으로 트랜잭션을 묶는다는 의미다. 

스프링에서의 @Transactional 은 인스턴스에서 처음으로 호출하는 메서드나 클래스의 속성을 따라가게 된다. 그래서 동일한 Bean안에 상위 메서드가 @Transactional 가 없으면 하위에는 선언이 되었다 해도 전이되지 않는다.

때문에 별도의 클래스(또는 Bean)으로 분리하거나 아니면 상위 메서드에 @Transactional 을 걸어주어야 한다. 이때 하위 메서드에 @Transactional 은 생략해도 된다.

 

 

3. TransactionManager에 ProxyDataSource 를 등록했는지 확인

대부분 transactionManager 를 다음과 같이 등록할 것이다

<bean id="transactionManager"
	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSourceMySQL" />
</bean>

위 설정에서 ref 에 보이는 dataSourceMySQL 의 Bean 은 Log4jdbcProxyDataSource 를 구현한 것이다.
만약 ref에 다가 dataSource 를 넣으면 @Transactional 이 작동하지 않는다
(@Transactional 은 Proxy 기반이기 때문)

해서 다음과 같이 transactionManager 와 ProxyDataSource 를 한 쌍으로 등록되었는지 확인한다.

<bean id="transactionManager"
	  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dataSourceMySQL" />
</bean>

<bean id="dataSourceMySQL" class="net.sf.log4jdbc.Log4jdbcProxyDataSource">
	<constructor-arg ref="dataSource" />
	<property name="logFormatter">
		<bean class="net.sf.log4jdbc.tools.Log4JdbcCustomFormatter">
			<property name="loggingType" value="MULTI_LINE" />
			<property name="sqlPrefix" value="" />
		</bean>
	</property>
</bean>

끝.

 

 

 

참조사이트: 

https://amitstechblog.wordpress.com/2011/05/20/spring-transactions-behavior-on-private-and-internal-methods/

 

Spring transaction behavior in private & internal methods

This post is to detail on the spring behavior when @Transactional annotations are applied on private methods or when transactional methods are called from within a method in the same class. Transac…

amitstechblog.wordpress.com

https://netframework.tistory.com/entry/Spring-Transactional에-대하여

 

Spring Transactional에 대하여.

Spring @Transactional에 대하여. " style="clear: both; font-size: 2.2em; font-weight: bold; margin: 0px 0px 1em; color: rgb(0, 0, 0); font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Lucida Sans..

netframework.tistory.com

 

반응형

댓글