본문 바로가기
개발/데이터베이스

MySQL 프로시저를 자바 코드로 이관

by Devsong26 2023. 11. 13.

MySQL 프로시저를 자바 코드로 이관하려고 합니다.

 

사유는 다음과 같습니다.

  • 프로시저 안에서 트랜잭션을 선언하게 될 경우 스프링 트랜잭션과 별개로 트랜잭션이 생성되어 원자성 위배
  • 버전관리가 어려움 
    • 프로시저의 새 버전이 나오면 _new, _v(n)등으로 새 버전의 프로시저를 생성
  • 디버깅이 어려움

 

아래와 같은 프로시저가 있다고 가정합니다.

CREATE PROCEDURE `procedure_v1` (
    IN p_status INT(2)
    , IN p_member_id BIGINT    
)
BEGIN 
    DECLARE v_status VARCHAR(10) DEFAULT NULL;
    
    SELECT status INTO v_status
    FROM member 
    WHERE id = p_member_id;
       
	IF v_status <> p_status THEN
    	// ... Logic

END

 

 

프로시저를 자바 코드로 이관할 때, CRUD 쿼리는 MyBatis 쿼리와 매핑된 메서드를 호출하는 것으로 변경하였고,

조건문 중 <>, != 은 if ( != ) 조건으로 수정하여 비즈니스 로직의 흐름을 작성했습니다.

 

이관한 코드는 아래와 같습니다.

@Service("procedureV1Service")
@AllArgsConstructor
public class ProcedureV1Service {

    private final MemberDao memberDao;    

    public void execute(Integer status, Long memberId) {
		   
           final Integer savedStatus = memberDao.findByMemberId(memberId).getStatus();
           
           if( ! savedStatus.equals(status) ) {
                // logic ...
           }
    }
    
}

 

 

테스트 코드를 통해 예외 케이스가 발생하지 않을 때까지 코드를 수정하였습니다.

개발계에서 1주일 모니터링 결과 예외가 발생되지 않아서 운영에 배포를 하게 됩니다.

 

그러나 운영에서 무수한 예외 케이스가 발생을 하여 롤백을 합니다.

 

원인은 기존 비즈니스 로직에서 status가 null 인 상태로 프로시저를 호출하는 케이스가 있었고, 

if( ! savedStatus.equals(status) ) 이 조건을 null이 아닌 savedStatus와 null인 status를 비교하여 참으로 조건문 안에 있는 쿼리들을 수행했기 때문이었습니다. 입력받은 status 값을 그대로 업데이트하는 쿼리가 있다 보니 롤백이 늦어졌으면 더 큰 운영 이슈가 될 뻔했습니다.

 

프로시저의 <> 연산자는 대상 중 하나라도 NULL일 경우 FALSE라서 여태까지 문제가 되지 않았던 것 입니다.

 

따라서 프로시저에서 자바 코드로 이관할 때는 아래의 경우를 잘 살펴야 합니다.

- 기존에 프로시저를 호출할 때 사용되는 파라미터 중 NULL 여부

- 자바 코드로 이관할 경우 데이터베이스에서의 연산자 동작과 자바 연산자의 동작이 일치하는지 반드시 확인

- 레거시 코드를 맹신하지 말 것

 

테스트도 열심히 했다고 생각했으나 기존 파라미터가 NULL인 경우를 놓쳐 버렸습니다.

테스트 케이스를 작성할 때도 더욱 신경써야 합니다.

 


 

 

프로시저를 이관할 때 기록을 하면 더욱 도움이 됩니다.

 

형식은 다음과 같습니다.

 

[개요]

  • 목적
  • 대상 프로젝트

 

[사용 테이블]

  • [데이터베이스 명]
    • [테이블 명]
      • C 횟수(또는 쿼리)
      • R
      • U
      • D

 

[호출 메서드]

  • [클래스].[메서드명]
    • [파라미터 NULL 확인 여부]

 

[대상 RESTful API]

  • 예시) GET, URI

 

[코드 수정 방법]

  • Before
  • After

 

[테스트 결과]

  • mock 테스트
    • 조건 테스트 
    • 쿼리 테스트
  • API 테스트

 

[프로시저 백업]

  • 코드