본문 바로가기
Spring

2023-01-04 트랜잭션 처리하기

by HTT 2023. 1. 4.
데이터베이스 트랜잭션(Database Transaction)

 

: 트랜잭션(Transaction)은 데이터베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위 또는 한꺼번에 모두 수행되어야 할 일련의 연산들을 의미한다.

데이터베이스 기능 중, 트랜잭션을 조작하는 기능은 사용자가 데이터베이스 완전성(integrity) 유지를 확신하게 한다. 단일 트랜잭션은 데이터베이스 내에 읽거나 쓰는 여러 개 쿼리를 요구하는데, 이때 중요한 것은 데이터베이스가 수행된 일부 쿼리가 남지 않는 것이다(commit 또는 rollback).

 

스프링에서 트랜잭션을 사용하기 위해서는 AOP개념을 알고있어야 한다.

 

 

 

 

AOP(Aspect Oriented Programming, 관심 지향 프로그래밍)

 

: AOP(Aspect Oriented Programming)는 관점 지향 프로그래밍으로 공통기능과 핵심기능을 분리하여 각각의 클래스로 작성 후, 적절한 시점에 적절한 핵심로직이 실행될 때 공통기능이 같이 실행되도록 처리하는 개념이다.

 

공통관심사항(cross-cutting concern)과 핵심관심사항(core concern)분리

 

- 공통관심사항
부가기능으로 많은 곳에서 사용하는 기능(로깅, 트랜잭션, 보안...)

 

- 핵심관심사항

비즈니스로직(업무로직)

 

 

 

 

AOP를 적용하기 위해 필요한 것


- 대상
: 공통기능을 적용할 핵심로직을 담고 있는 메소드(혹은 클래스)

- Advice
: 공통기능을 구현한 클래스

- 포인트컷
: aop를 정의할 때 타겟이 어떤 것인지 정의하기 위한 표현식
  
  execution(정규표현식)
  execution([접근제어자] 리턴타입 패키지명 클래스명 메소드명 매개변수)
  
  execution(void com.multi.erp
  
  공통
  * => 모든 것
  .. => 없거나 1이거나 모든 것에
  
  리턴타입
  * => 모든 리턴타입을 의미
  int => int를 리턴하는 메소드
  !void => void가 아닌 모든 메소드
  
  패키지
  com.multi.erp는 void com.multi.erp패키지라는 것을 정확하게 명시
  com.multi.erp.. => com.multi.erp로 시작하는 모든패키지
  com.multi.erp.*.get* => com.multi.erp패키지의 모든 클래스에서 get으로 시작하는 메소드에 적용
  com.multi.erp.*.*.get* => com.multi.erp패키지로 시작하는 4단계 패키지의 모든 클래스에서 get으로 시작하는 메소드
  
  메소드
  *(..) => 모든 메소드
  get*(..) => get으로 시작하는 모든 메소드
  
  매개변수
  (..) => 모든 매개변수
  (*) => 매개변수 1개짜리 메소드
  (Integer,..) => 한 개 이상의 매개변수를 갖는 메소드 단, 첫 번째 매개변수는 타입이 Integer
  (Integer,*) => 두 개의 매개변수를 갖는 메소드, 첫 번째 매개변수는 타입이 Integer

 

 

 

 

 

예제

 

Aspectj를 이용해 트랜잭션 처리하기

 

사용방법

① xml로 작업

② 클래스를 직접 만들어 작업

 

1. pom.xml에 라이브러리 추가

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>4.2.4.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.2.4.RELEASE</version>
</dependency>

 

 

2. 스프링 설정파일에 빈등록하기

<!-- 1. transactionManager객체등록 -->
<beans:bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <beans:property name="dataSource" ref="ds"/>
</beans:bean>
<!-- 2. Advice등록 -->
<!-- Exception이 발생하면 rollback처리를 하겠다는 의미 -->
<tx:advice id="txadvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method  name="insert" rollback-for="Exception"/>
    </tx:attributes>
</tx:advice>
<!-- aop처리 -->
<aop:config>
    <aop:pointcut expression="execution(* com.multi.erp.board..*Service*.*(..))" id="txpointcut"/>
    <aop:advisor advice-ref="txadvice" pointcut-ref="txpointcut"/>
</aop:config>
<!-- aop작업을 수행하는 proxy가 자동으로 감지해서 aop등록된 것을 확인하고 실행될 수 있도록 처리 -->
<aop:aspectj-autoproxy/>

게시글을 insert할 때 예외가 발생하면 rollback처리되도록 하기

 

 

3. 메소드 정의(공통로직이 어떤 핵심로직에서 실행될 것인지 정의, 어느 시점에 실행될 것인지 정의)

public class TxAdvice {
	@Autowired 
	private PlatformTransactionManager transactionManager;
	@Pointcut("execution(* com.multi.erp.board..*Service*.*(..))")
	public void insertTx() {
		
	}
	@Around("insertTx()")
	public Object apllyTx(ProceedingJoinPoint joinpoint) throws Throwable {
		TransactionStatus transactionStatus =  transactionManager.getTransaction(new DefaultTransactionDefinition());
		try {
			Object object = joinpoint.proceed();
			transactionManager.commit(transactionStatus);
			return object;
		} catch (RuntimeException e) {
			//오류상황
			transactionManager.rollback(transactionStatus);
			throw e;
		}
	}
}

=> Advice는 공통관심사항을 정의할 클래스이며, execution은 Advice를 적용할 메소드를 명시할 때 사용한다. 표현식 execution을 사용해 트랜잭션이 적용될 패키지, 클래스, 메소드를 정의해준다.

@Around어노테이션은 메소드 실행 전, 메소드 실행 후에 모두 호출되기 때문에 트랜잭션처리를 해주는 메소드에 정의해준다.

 

트랜잭션을 서로 다른 DB에 적용하기 위해서 트랜잭션도 인터페이스를 이용해 처리해줘야 한다.

PlatformTransactionManager는 스프링 트랜잭션의 핵심 인터페이스로 트랜잭션의 경계를 지정하는 데 사용한다. 트랜잭션이 어디서 시작하고 종료하는지, 종료할 때 정상 종료(커밋)인지 비정상 종료(롤백)인지를 결정하는 것이다. 스프링에서는 시작과 종료를 트랜잭션 전파 기법을 이용해 자유롭게 조합하고 확장할 수 있다.

인터페이스 TransactionStatus는 트랜잭션의 상태를 관리하는 역할을 하며, PlatformTransactionManager에서 트랜잭션을 commit할지 rollback할지를 결정하기 위해 사용한다.

 

나는 모든 리턴타입을 포함시키며, 패키지 com.multi.erp.board로시작하고 Service가 들어가는 클래스의 모든 메소드와 매개변수를  처리하였다.

 

 

실행결과 => 게시글을 등록할 때(DB에 넘길 때) 파일첨부를 하지 않으면 에러가 뜨면서 정확하지 않은 정보가 저장됐지만, 트랜잭션 처리 후엔 rollback되면서 정보가 넘어가지 않았다.

 

 

insert메소드를 실행하면서(게시글등록) 발생하는 500번 에러는 insert메소드가 정의된 컨트롤러에서 null체크를 하여 처리해준다. 

@RequestMapping(value = "/board/write.do",method = RequestMethod.POST)
public String insert(BoardDTO board,HttpSession session) throws IllegalStateException, IOException {
	List<MultipartFile> files = board.getFiles();
	String path = WebUtils.getRealPath(session.getServletContext(), "/WEB-INF/upload");
	List<BoardFileDTO> boardfiledtolist = fileuploadService.uploadFiles(files, path);
	if(boardfiledtolist.equals(null)) {
		int count = 1;
		for(BoardFileDTO boardfiledto:boardfiledtolist) {
            boardfiledto.setFile_no(count+"");
            count++;
        }
        service.insert(board, boardfiledtolist);
    }else {
        service.insert(board);
    }
}

 

 

 

 

 

참고

https://coding-factory.tistory.com/226

http://ojc.asia/bbs/board.php?bo_table=LecSpring&wr_id=367 

https://springsource.tistory.com/127

'Spring' 카테고리의 다른 글

Maven이란?  (0) 2023.01.16
2023-01-16 JSON, AJAX 랭킹정보  (0) 2023.01.16
2022-12-16,19 mongodb  (0) 2022.12.19
2022-12-17 Intercepter란?  (0) 2022.12.17
파일업로드로직 메소드  (0) 2022.12.15