스프링 트랜잭션 관리 - @Transactional 어노테이션 활용

트랜잭션 관리 개요

트랜잭션은 데이터베이스 작업의 단위로, 여러 개의 연산을 하나의 논리적인 작업單位로 묶습니다. 모든 연산이 성공하면 커밋(commit)되고, 하나라도 실패하면 롤백(rollback)되어 이전 상태로 복구됩니다.

사례: 부서 삭제 시 연관 데이터 처리

부서를 삭제할 때 해당 부서에 속한 직원들도 함께 삭제해야 하는 경우가 있습니다. 이때 트랜잭션을 사용하면 부서 삭제와 직원 삭제를 하나의 원자적 연산으로 처리할 수 있습니다.

@Transactional 어노테이션

Spring에서 트랜잭션 관리는 @Transactional 어노테이션을 통해 쉽게 처리할 수 있습니다. 이 어노테이션을 메서드나 클래스에 적용하면 해당 범위内的 데이터베이스 작업이 트랜잭션으로 관리됩니다.

트랜잭션 로그 활성화

트랜잭션 동작 과정을 확인하려면 application.yml에 로깅 설정을 추가하면 됩니다.

코드 예제: 부서 삭제 시 트랜잭션 롤백

다음은 부서 삭제 시 연관된 직원 데이터도 함께 삭제하는 예제입니다. 중간에 오류가 발생하면 전체 트랜잭션이 롤백됩니다.

EmpMapper

package com.example.mapper;

import com.example.entity.Emp;
import org.apache.ibatis.annotations.*;

import java.time.LocalDate;
import java.util.List;

/**
 * 직원 데이터 매퍼
 */
@Mapper
public interface EmpMapper {

    /**
     * 직원 목록 조회
     */
    public List<Emp> search(@Param("name") String name,
                          @Param("gender") Short gender,
                          @Param("begin") LocalDate begin,
                          @Param("end") LocalDate end);

    /**
     * 다중 삭제
     * @param ids 삭제할 직원 ID 목록
     */
    void delete(@Param("ids") List<Integer> ids);

    /**
     * 직원 추가
     * @param emp 직원 정보
     */
    @Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) " +
            "VALUES(#{username},#{name},#{gender},#{image},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime}) ")
    void insert(Emp emp);

    /**
     * ID로 직원 조회
     * @param id 직원 ID
     * @return 직원 정보
     */
    @Select("select * from emp where id = #{id}")
    Emp getById(Integer id);

    /**
     * 직원 정보 수정
     * @param emp 직원 정보
     */
    void update(Emp emp);

    /**
     * 아이디와 비밀번호로 직원 조회
     * @param emp 검색 조건
     * @return 직원 정보
     */
    @Select("select * from emp where username = #{username} and password = #{password}")
    Emp getByUsernameAndPassword(Emp emp);

    /**
     * 부서에 속한 직원 삭제
     * @param deptId 부서 ID
     */
    @Delete("delete from emp where dept_id = #{deptId}")
    void deleteByDeptId(Integer deptId);
}

DeptServiceImpl

package com.example.service.impl;

import com.example.mapper.DeptMapper;
import com.example.mapper.EmpMapper;
import com.example.entity.Dept;
import com.example.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;
    @Autowired
    private EmpMapper empMapper;

    @Override
    public List<Dept> search() {
        return deptMapper.list();
    }

    @Transactional
    @Override
    public void delete(Integer id) {
        deptMapper.deleteById(id);

        int result = 1 / 0;

        empMapper.deleteByDeptId(id);
    }

    @Override
    public void add(Dept dept) {
        dept.setCreateTime(LocalDateTime.now());
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.insert(dept);
    }

    @Override
    public Dept getByID(Integer id) {
        return deptMapper.getByID(id);
    }

    @Override
    public void update(Dept dept) {
        dept.setUpdateTime(LocalDateTime.now());
        deptMapper.update(dept);
    }
}

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/tlias
    username: root
    password: root
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 100MB

mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    map-underscore-to-camel-case: true

aliyun:
  oss:
    endpoint: *************
    accessKeyId: *************
    accessKeySecret: *************
    bucketName: *************

logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

실행 결과

delete 메서드에서 의도적으로 예외(1/0)를 발생시켰을 때, 트랜잭션이 관리되고 있으므로 전체 작업이 롤백됩니다. 부서 데이터와 해당 부서에 속한 직원 데이터 모두 삭제되지 않고 원래 상태를 유지합니다. 이것이 트랜잭션의 원자성(atomicity)을 보장하는 Spring의 특징입니다.

태그: Spring transaction rollback MyBatis Annotation

6월 29일 16:08에 게시됨