본문 바로가기
Spring

[스프링부트] 10. 트랜잭션 처리

by moca7 2024. 11. 6.

 

 

 

ㅁ (스프링) root-context.xml

 

 
<?xml version="1.0" encoding="UTF-8"?>
 
  <!-- mybatis 사용을 위한 빈 3개 -->
  <bean class="org.apache.commons.dbcp2.BasicDataSource" id="dataSource" destroy-method="close">
    <!-- <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
         <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe" /> -->
    <property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy" />
    <property name="url" value="jdbc:log4jdbc:oracle:thin:@localhost:1521:xe" />
    <property name="username" value="spring" />
    <property name="password" value="spring" />
  </bean>


  <bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
    <property name="configLocation" value="classpath:config/mybatis-config.xml" />
    <property name="dataSource" ref="dataSource" />
  </bean>


  <bean class="org.mybatis.spring.SqlSessionTemplate" id="sqlSession">
    <constructor-arg ref="sqlSessionFactory" />
  </bean>  
 
 
 
  <!-- 트랜잭션 처리용 aop를 위한 구문 3개 -->
  <!-- AOP를 이용한 트랜잭션 처리 -->
  <!-- 1) 트랜잭션 매니저 빈으로 등록 (dataSource 객체 필요함) -->
  <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="txManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
 
 
  <!-- 2) 트랜잭션 Advice 등록 -->
  <tx:advice transaction-manager="txManager" id="txAdvice">
    <tx:attributes>
      <tx:method name="*" /> <!-- pointcut의 모든 메소드에서 실행하겠다. (삽입, 수정, 삭제, 목록조회, 상세조회 등의 메소드가 있음) -->
      <tx:method name="select*" read-only="true" />  <!-- 단, select로 시작하는 메소드는 실행하지 않는다. (삽입, 수정, 삭제 메소드만 실행됨) -->
    </tx:attributes>
  </tx:advice>
 
 
  <!-- 3) AOP 등록 -->
  <aop:config>
    <aop:pointcut expression="execution(* com.br.spring.service.*Impl.*(..))" id="txPointcut" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
  </aop:config>
 
 
 
  <!-- 파일 업로드를 위한 빈 등록 -->
  <!-- 주의사항 : 빈 이름을 내 마음대로 하면 안되고 반드시 multipartResolver로 해야 한다. -->
  <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
    <property name="maxUploadSizePerFile" value="10485760" />
    <property name="maxUploadSize" value="104857600" />
    <property name="defaultEncoding" value="UTF-8" />
  </bean>
   
</beans>
 

 

 

- 스프링에서 트랜잭션 처리를 aop로 했었다.

 

 

 

 

 

ㅁ BoardSelectImpl

 

 
package com.br.boot.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.br.boot.dao.BoardDao;
import com.br.boot.dto.AttachDto;
import com.br.boot.dto.BoardDto;
import com.br.boot.dto.PageInfoDto;
import com.br.boot.dto.ReplyDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {

  private final BoardDao boardDao;
 

  @Override
  public int selectBoardListCount() {
    return boardDao.selectBoardListCount();
  }

  @Override
  public List<BoardDto> selectBoardList(PageInfoDto pi) {
    return boardDao.selectBoardList(pi);
  }

  @Override
  public int selectSearchListCount(Map<String, String> search) {
    return boardDao.selectSearchListCount(search);
  }

  @Override
  public List<BoardDto> selectSearchList(Map<String, String> search, PageInfoDto pi) {
    return boardDao.selectSearchList(search, pi);
  }

  @Transactional
  @Override
  public int insertBoard(BoardDto b) { // 컨트롤러에서 BoardDto에 작성자, 제목, 내용, 첨부파일 list도 담아서 넘겼다.
   
    int result = boardDao.insertBoard(b);
   
    List<AttachDto> list = b.getAttachList();
 
 
    if(result > 0 && !list.isEmpty()) {
 
      result = 0;
 
      for(AttachDto attach : list) {
        result += boardDao.insertAttach(attach);
      }
 
    }
   
    return result;
  }

  @Override
  public int updateIncreaseCount(int boardNo) {
    return boardDao.updateIncreaseCount(boardNo);
  }
 

  @Override
  public BoardDto selectBoard(int boardNo) {
    return boardDao.selectBoard(boardNo);
  }

  @Override
  public int deleteBoard(int boardNo) {
    return boardDao.deleteBoard(boardNo);
  }

  @Override
  public List<ReplyDto> selectReplyList(int boardNo) {
    return boardDao.selectReplyList(boardNo);
  }

  @Override
  public int insertReply(ReplyDto r) {
    return boardDao.insertReply(r);
  }
 
 

  @Override
  public List<AttachDto> selectDelAttach(String delFileNo[]) {
    return delFileNo == null ? new ArrayList<>() : boardDao.selectDelAttach(delFileNo);
  }

  @Transactional
  @Override
  public int updateBoard(BoardDto b, String[] delFileNo) {
   
    // 1) board 테이블에 update
    int result1 = boardDao.updateBoard(b);
   
    // 2) attachment 테이블에 delete
    int result2 = 1; // delFileNo이 null일 때도 ~
    if(result1 > 0 && delFileNo != null) {
      result2 = boardDao.deleteAttach(delFileNo);
    }
   
    // 3) attachment 테이블에 insert
    List<AttachDto> list = b.getAttachList();
    int result3 = 0;
    for(AttachDto at : list) {
      result3 += boardDao.insertAttach(at);
    }
   
   
    // 성공에 대한 조건- result1이 1이어야 하고, result2가 0보다 커야 하고, result3가 list의 사이즈와 동일해야 한다.

    return result1 == 1
          && result2 > 0
            && result3 == list.size()
              ? 1 : -1;
   
  }
 
 
 

  @Override
  public int deleteReplyCompletely() {
    return boardDao.deleteReplyCompletely();
  }
 
}
 

 

 

- 트랜잭션 처리가 필요한 메소드 위에 @Transactional을 붙이면 된다. 2개 붙였다.

- 만약 이 클래스의 모든 메소드에 필요하면 클래스 상단에 @Transactional을 부이면 이 안의 모든 메소드가 다 트랜잭션 처리가 된다. 

- 회사가면 서비스를 분리하는 경우도 있다.

트랜잭션 처리가 필요한 서비스 따로 필요하지 않은 서비스 따로 분리하는 경우가 있다.

이렇게 분리를해서 서비스 안의 모든 메소드에 트랜잭션 처리가 필요하면 클래스 상단에 @Transactional을 붙이면 된다.

 

 

 

ㅁ 마이그레이션은 끝났다.

 

ㅁ 부트프로젝트 작업은 끝.

 

 

 

'Spring' 카테고리의 다른 글

스프링 정리 - MVC  (0) 2024.11.13
디비  (1) 2024.11.06
[스프링부트] 9. 웹소켓  (2) 2024.11.06
[스프링부트] 8. Scheduler  (0) 2024.11.06
[스프링부트] 7. Interceptor  (0) 2024.11.06