본문 바로가기
Spring

[Spring Boot] JPA @Transactional과 변경 감지 Update

by noddu 2024. 7. 11.
728x90
반응형

 

 

 

@Transactional 적용 x

<Plan> 테이블의 모습입니다.

<Plan> 테이블의 PLAN_ID가 1인 행의 데이터를 update 하려고 합니다.

 

 

PlanController

@ResponseBody
@PatchMapping("")
public Plan updatePlan(@RequestBody Plan plan) {
    return planService.update(plan.getPlanId(), plan.getPlanTitle(), plan.getBudget(), plan.getPlanStartDate(), plan.getPlanEndDate());
}

먼저 @PatchMapping을 사용해 업데이트 요청을 받을 Controller를 작성합니다.

 

 

PlanService

@Service
@RequiredArgsConstructor
public class PlanService {

private final PlanRepository planRepository;

    // @Transactional
    public Plan update(Long planId, String planTitle, int budget, LocalDateTime planStartDate, LocalDateTime planEndDate) {
        return planRepository.update(planId, planTitle, budget, planStartDate, planEndDate);
    }
}

Service에서는 PlanRepository update() 메서드를 여러 데이터를 넘기며 호출합니다

이때 @Transaction은 주석처리 한 상태로 해보겠습니다.

 

 

PlanRepository

@Repository
@RequiredArgsConstructor
public class PlanRepository{
	
    private final EntityManager em;

    public Plan update(Long planId, String planTitle, int budget, LocalDateTime planStartDate, LocalDateTime planEndDate) {
            Plan plan = em.find(Plan.class, planId);
            plan.setPlanTitle(planTitle);
            plan.setBudget(budget);
            plan.setPlanStartDate(planStartDate);
            plan.setPlanEndDate(planEndDate);
            return plan;
    	}
    }

PlanRepository에서 EntityManger를 사용해서 데이터를 업데이트합니다.

update()함수는 파라미터로 넘어온 planId로 해당 행을 찾아서 각 데이터를 setXXX 처리하고

그 필드가 변경된 Plan 인스턴스를 반환합니다.

 

 

 

업데이트가 되는지 PostMan으로 Patch 요청을 보냅니다.

planTitle만 "changedTitle"로 변경해보겠습니다.

 

 

<Plan> 테이블을 다시 확인해보니

PLAN_ID가 1인 행의 PLAN_TITLE이 'title'로 유지하고 있습니다.

 

 

 

@Transactional 적용

PlanService

@Transactional
public Plan update(Long planId, String planTitle, int budget, LocalDateTime planStartDate, LocalDateTime planEndDate) {
    return planRepository.update(planId, planTitle, budget, planStartDate, planEndDate);
}

PlanService의 update() 메서드에 @Transactional 어노테이션을 적용하고 다시 Patch 요청을 보내봅시다.

 

 

PLAN_ID가 1인 행의 PLAN_TITLE이 'changeTitle'로 변경된 걸 볼 수 있습니다.

 

 

 

테스트코드 - @Transactional 적용 x

PlanServiceTest

@Test
//@Transactional
public void 계획_수정() {

    //given
    Plan findPlan = planService.findById(1L);

    //when
    Plan changePlan = planService.update(findPlan.getPlanId(), "changedTitle", 50000,
            LocalDateTime.of(2018, 10, 22, 14, 00, 00, 00),
            LocalDateTime.of(2020, 6, 4, 13, 00, 00, 00)
    );

    //then
    Plan findPlan2 = planService.findById(changePlan.getPlanId());
    log.info(findPlan2.getPlanTitle());
    Assertions.assertThat(findPlan2.getPlanTitle()).isEqualTo(changePlan.getPlanTitle());
}

테스트코드에서도 @Tranactional을 주석처리하고 실행해보겠습니다.

 

findById()를 통해 얻은 제목을"changedTitle"로 예상했지만 결과는 바뀌지 않은 "title"로 유지되어 테스트에 실패합니다.

 

 

테스트코드 - @Transactional 적용 x

PlanServiceTest

@Test
@Transactional
public void 계획_수정() {

    //given
    Plan findPlan = planService.findById(1L);

    //when
    Plan changePlan = planService.update(findPlan.getPlanId(), "changedTitle", 50000,
            LocalDateTime.of(2018, 10, 22, 14, 00, 00, 00),
            LocalDateTime.of(2020, 6, 4, 13, 00, 00, 00)
    );

    //then
    Plan findPlan2 = planService.findById(changePlan.getPlanId());
    log.info(findPlan2.getPlanTitle());
    Assertions.assertThat(findPlan2.getPlanTitle()).isEqualTo(changePlan.getPlanTitle());
}

이번엔 @Tranactional 활성화하고 다시 테스트를 실행해보면

 

테스트가 정상적으로 성공했습니다.

 

 

 

왜 @Transactional를 해야하나?

JPA에서는 @Transactional 어노테이션을 통해 트랜잭션을 관리합니다.

트랜잭션이 시작되면 엔티티 매니저는 해당 트랜잭션 범위 내에서 엔티티의 변경 사항을 추적하고,

트랜잭션이 커밋될 때 변경 사항을 데이터베이스에 반영합니다.

 

@Transactional이 없으면 엔티티의 변경 사항이 영속성 컨텍스트에 반영되지 않으며,

데이터베이스에 변경 사항이 저장되지 않습니다.

 

@Transactional 어노테이션을 사용해 트랜잭션 범위 내에서 엔티티를 수정하고 이를 커밋해야만 변경 사항이 데이터베이스에 반영됩니다.

반응형