개발을 하다보면 어느 순간 쿼리 한줄로는 끝낼 수 없는 상황에 부딪치게 되는데

이렇게 단일 쿼리로 해결할 수 없는 로직을 처리할 때 필요한 기술이 바로 transaction이다. 

2개 이상의 쿼리를 하나의 커넥션으로 묶어 DB에 전송하고 이 과정에서 어떠한 에러가 발생할 경우

자동으로 모든 과정을 원래 상태로 돌려놓는다. 

이 로직을 JAVA에서 처리한다.

 

transaction은 "쪼개질 수 없는 업무처리의 단위"를 의미한다.

예를 들어, ATM으로 계좌이체를 하면 내 계좌의 잔액에서 이체한 금액만큼 빼는 일과, 상대 계좌의 잔액에서 해당 금액만큼 더하는 일은 쪼개져서는 안된다.

즉 하나의 업무로 함께 진행되야 하는 일이다.

"더이상 쪼갤 수 없다는" 표현 때문에 이를 원자성을 보장해야 한다고 얘기한다.

참고로, transaction에는 4가지 특성이 있다.

원자성Atomicity, 일관성consistency, 고립성Isolation, 지속성durability

쪼갤 수 없기 때문에 일부만 동작해선 안된다. 

부분 작업들 여러 개가 모여진 이러한 transaction을 처리하기 위해 DB는 다음과 같은 기술을 제공한다. 

-롤백(Rollback): 부분 작업이 실패하면 transaction 실행 전으로 되돌린다.

-커밋(Commit): 모든 부분작업이 정상적으로 완료하면 이 변경사항을 한꺼번에 DB에 반영한다.

 

++첫 번째, Custom Exception으로 transaction거는방법!

내가 만든 Exception을 사용하여 강제적 rollback을 하고싶으면 이 방법을 사용한다.

반드시 try catch가 걸려있어야 한다. 

catch영역에다가 TransactionAspectSupport.currentTransactionStatus( ).setRollbackOnly( ); 적어준다.

이렇게 작성해 주면 exception 발생 시 해당메서드를 rollback 시킬수 있게 된다. TransactionAspectSupport.currentTransactionStatus( ).setRollbackOnly( )가 실행될 때 

바로 rollback이 진행되지는 않는다.

setRollbackOnly( )는 속성만 변경을 하는 것이기 때문에 실제 rollback이 일어나는 시점은

commit이 되기 직전에 수행되게 된다.

 

ex)

package egovframework.example.join.service.impl;

@Service
public class JoinServiceImpl implements JoinService {
	
	@Resource
	private JoinMapper joinMapper;

	@Override
	public void saveJoinMbr(JoinVO joinVO) {
		
		try {
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			
			for (int i = 0; i < termsMngList.size(); i++) {
				Integer	termsMngNo	= (Integer) termsMngList.get(i).get("termsMngNo");
				
				joinMapper.insertTermsAgr();
				
				throw new Exception();
			}
		} catch (Exception e) {
			e.printStackTrace();
			
			TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
		}
		
	}

}

그리고 context-transaction.xml에 가서 sample이란 업무폴더만 transaction이 적용되게 되어있다. 

여기서 **바꿔주면 모든 업무에 transaction이 걸린다.

 

ex)

<aop:config>
	<aop:pointcut id="requiredTx" expression="execution(* egovframework.example.sample..impl.*Impl.*(..))"/>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />
</aop:config>

--------------------------------------------------------------------------------------------------------

<aop:config>
	<aop:pointcut id="requiredTx" expression="execution(* egovframework.example.**..impl.*Impl.*(..))"/>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />
</aop:config>

프로그램을 실행하고 오류가 나지 않는다면 try~catch구문을 지워준다.

 

 

++ 두 번째, AOP 방식으로 transaction 거는방법이다.
context-transaction.xml에 가서 서비스명으로 트랜잭션을 거는 방법이다. 

ex)

<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
  		<tx:method name="*" rollback-for="Exception"/>
	</tx:attributes>
</tx:advice>

<aop:config>
	<aop:pointcut id="requiredTx" expression="execution(* egovframework.example.**..impl.*Impl.*(..))"/>
	<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />
</aop:config>

---------------------------------------------------------------------------------------------


<tx:advice id="txAdvice" transaction-manager="txManager">
	<tx:attributes>
		<tx:method name="*Tx" rollback-for="Exception"/>
	</tx:attributes>
</tx:advice>

<aop:config>
	<aop:pointcut id="requiredTx" expression="execution(* egovframework.example.**..impl.*Impl.*(..))"/>
  	<aop:advisor advice-ref="txAdvice" pointcut-ref="requiredTx" />
</aop:config>



 

그리고 관련 메서드명 뒤에 context-transaction.xml에서 만든 서비스명을 붙여준다. throws Exception도 붙여준다.

ex)

package egovframework.example.join.service.impl;

@Service
public class JoinServiceImpl implements JoinService {
	
	@Resource
	private JoinMapper joinMapper;

	@Override
	public void saveJoinMbr(JoinVO joinVO) {
		
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			
			for (int i = 0; i < termsMngList.size(); i++) {
				Integer	termsMngNo	= (Integer) termsMngList.get(i).get("termsMngNo");
				
				joinMapper.insertTermsAgr();
			}
		
	}

}
---------------------------------------------------------------------------------------------------------


package egovframework.example.join.service.impl;

@Service
public class JoinServiceImpl implements JoinService {
	
	@Resource
	private JoinMapper joinMapper;

	@Override
	public void saveJoinMbrTx(JoinVO joinVO) throws Exception{
		
			HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
			
			for (int i = 0; i < termsMngList.size(); i++) {
				Integer	termsMngNo	= (Integer) termsMngList.get(i).get("termsMngNo");
				
				joinMapper.insertTermsAgr();
			}
		
	}

}



++세 번째는 선언전 transaction을 거는 방법이다.

@transaction을 사용한다. 어노테이션 방식의 경우 선언으로 지정할 수 있다. (@ : 어노테이션)

먼저, DataSourceTransaction클래스의 tx-annotaion-driven아이디를 기입한다.

<tx:annotation-driven 뒤에 transaction-manager="bean id"/>로 만들어주는 것이다.

 그리고 @Transactiona에도 bean id를 지정해준다.

 

ex)

<!-- datasource가 있는 xml-->
 
<tx:annotation-driven transaction-manager="myTransactionManager"/>
 <bean id="myTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="egov.dataSource" />
</bean>


-----------------------------------------------------------------------------------------------


/* transaction 사용할 자바 클래스 */
@Transaction("myTransactionManager")
public int testMethod(){
    
    int myCount = testService.insertMy;
    return myCount;
}

 

선언적 transaction을 사용할 때 checked예외가 발생하면 rollback을 하지 않는다.
Q. 왜 Spring에서는 checked예외일 때 rollback을 하지 않을까?

A. 그 이유는 Spring의 예외전략은 unchecked예외다.

기본 java에서는 데이터베이스에 쿼리를 보내거나 연결을 할 때 error가 발생하면 checked예외를 던진다.

보통 일반적으로 SqlException을 던지고 이 예외는 checked예외다.

  하지만 Spring에서 checked예외를 unchecked 예외로 포장해서 우리에게 던져준다.

그 이유는 데이터베이스에 예외가 났을 경우 거의 99프로가 복구 할 수 없는 예외이다.

개발자의 잘못으로 쿼리가 잘 못 작성되었거나 서버가 죽어있다면 복구 불가능한 예외이다.

그래서 Spring에서는 복구할 수 없는 예외로 판단하여 굳이 checked예외를 던지지 않고 unchecked예외로 포장해서 던져준다.

이 뿐만 아니라 Spring에서는 거의 대부분 unchecked예외를 던지지 checked예외를 던지는 경우는 드물다.

 

대부분의 웹 프로그래밍을 하면 checked예외는 사용하지 않겠지만 라이브러리, 프레임워크 등 코어 개발자라면

checked예외를 종종 사용한다.

예를들어 꼭 알려야 하는 예외(파일을 쓰지 못한다거나, 서버와 커넥션이 안되었거나 기타 등등)가 발생하였을 경우에는 checked예외를 사용해서 상위 메서드에게 알려줘야 한다. 우리는 예외를 잡아서 포장해서 던지면 된다. 

 

 

 

 

 

 

출처 :  한큐의 자바 수강내용

출처: https://sjh836.tistory.com/11 [빨간색코딩]

출처 : http://egloos.zum.com/springmvc/v/495798

출처: https://krespo.net/159 [KRESPO.NET]

출처:https://zorba91.tistory.com/entry/Transactional%EC%84%A0%EC%96%B8%EC%A0%81-%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98-bean-Id-%EC%A7%80%EC%A0%95%ED%95%B4%EC%84%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0   

출처 : http://wonwoo.ml/index.php/post/1542

' > Debugging' 카테고리의 다른 글

개발자도구에서 디버깅(debugging) 하기  (0) 2019.06.09

debugging이란 오류수정, 프로그램의 잘못을 찾아내고 고치는 작업이다. 

데이터가 잘 들어갔는지 안 들어갔는지 확인하는데 사용한다. 

 

debugging종류는 Interactive debugging, 컨트롤 분석, 로그 파일 분석 등이 있다.

Java와 Javascript로 두 군데에 로직을 작성한다. 

 

먼저, 웹 브라우저에 내장된 개발자 도구에서 debugging 하는 방법이다.

크롬(웹 브라우저)을 열고 F12버튼을 클릭해서 사용한다.

 

개발자도구 실행모습

개발자 도구에서 debugging은 Sources탭에서 발생한다.

Sources탭은 직접 수정이 불가능하다. 

Sources탭에 있는 watch탭은 Elements에 갈 필요없이 원하는 것을 호출 할 수 있다.

그 변수에 잘 들어갔는지 확인하는 모드에는 alert도 있지만 개발자도구에 있는 break point사용해본다.  
이것이 debugging의 핵심이다. 

break point는 로직을 실행하다 내가 설정한 break point에서 멈춘다. 

break point는 반드시 개발자도구가 켜져있는 상태에서만 동작을 한다. 

 

(실행화면 나중에 첨부)


핵심 단축키에 대해서 알아보겠다.  
크롬에서 F12누르고 
  F10키를 누르면 에러가 발생했을 때 break point에서 멈추고 에러가 없어지면 한 줄 내려간다. 
  F8키를 누르면 다음 break point까지 이동하는데 중간을 다 실행시키고 이동한다. 

스크립트에 함수를 선언하면 그 부분에는 break point가 걸리지 않는다. 
무조건 실행한다고 해서 break point에 멈추지 않는다. 

 

출처 :  한큐의 자바 수강내용

+ Recent posts