- Today
- Yesterday
- Total
메이쁘
[Spring][mybatis] insert 쿼리 실행 후 리턴값으로 원하는 값을 얻고자 하는 법 + 적용 이슈 해결(selectKey) 본문
[Spring][mybatis] insert 쿼리 실행 후 리턴값으로 원하는 값을 얻고자 하는 법 + 적용 이슈 해결(selectKey)
메이쁘 2022. 2. 4. 22:56안녕하세요?
보통 java persistence framework로 ORM방식인 JPA를 많이들 사용하시는데요.
현재 프로젝트에서는 SQL Mapper 방식인 mybatis로 DB연동을 합니다.
API 개발 도중,
insert한 후, insert할 때 사용했던 PK값을 가지고 와서 이 PK와 연계된 테이블에 FK로 값을 insert하는 그런 로직을 만들어야 했습니다.
이를 위해 insert 후 해당 값을 다시 select해서 PK값을 가져오는건 비효율적이라고 생각했습니다.
다른 방법이 있지 않을까 찾다가,
insert 후 결과로 특정 값을 select 해서 받아올 수 있는 방법을 찾게 되서 적용시켰는데요.
바로, selectKey 입니다.
핵심만 말씀드리면,
<insert>
<selectKey></selectKey>
</insert>
방식으로 xml에 쿼리를 만들어두면, selectKey 태그에 설정한 값을 가져올 수 있습니다.
이와 같이 말이죠..
정리하자면,
insert(update, delete도 가능합니다) 쿼리 실행 전후로 원하는 값을 select하여 결과를 리턴받기 위해서
<selectKey> 태그를 사용합니다.
1. selectKey 적용
제가 첨부한 위 캡쳐를 보시면,
<selectKey keyColumn="RFNM_BNAC_RGST_SRMB" keyProperty="rfnmBnacRgstSrmb" order="AFTER" resultType="java.lang.String">
라고 작성했는데요.
간단합니다.
keyColumn 속성은 <selectKey></selectKey> 내부에 작성한 SELECT 문의 조회 결과 컬럼과 일치합니다.
리턴하고자 하는 컬럼값을 넣으면 됩니다.
keyProperty 속성은 insert에 사용한 parameter 객체 안에 맵핑할 변수입니다.
저같은 경우에는 parameter로 넣은 object에
String rfnmBnacRgstSrmb;
로 선언했기 때문에, 위 변수명을 일치하게 입력했습니다.
order 속성은 크게 BEFORE과 AFTER가 있는데요.
단순하게 BEFORE는 insert 실행 전에 selectKey를 호출하여 결과 값을 keyProperty에 저장해두는 것이고,
AFTER는 insert 실행 후에 selectKey를 호출하여 결과 값을 keyProperty에 저장해두는 것입니다.
(resultType은 추후 설명드리겠습니다.)
추가적인 설명은, 다른 블로그에 작성된 내용을 참고해주시면 감사하겠습니다!
keyProperty: selectKey구문의 결과가 셋팅될 대상 프로퍼티.
keyColumn: 리턴되는 결과셋의 칼럼명은 프로퍼티에 일치한다. 여러개의 칼럼을 사용한다면 칼럼명의 목록은 콤마를 사용해서 구분한다.
resultType: 결과의 타입. 마이바티스는 이 기능을 제거할 수 있지만 추가하는게 문제가 되지는 않을것이다. 마이바티스는 String을 포함하여 키로 사용될 수 있는 간단한 타입을 허용한다.
order: BEFORE 또는 AFTER를 셋팅할 수 있다. BEFORE로 설정하면 키를 먼저 조회하고 그 값을 keyProperty 에 셋팅한 뒤 insert 구문을 실행한다. AFTER로 설정하면 insert 구문을 실행한 뒤 selectKey 구문을 실행한다. 오라클과 같은 데이터베이스에서는 insert구문 내부에서 일관된 호출형태로 처리한다.
statementType: STATEMENT, PREPARED 또는 CALLABLE중 하나를 선택할 수 있다. 마이바티스에게 Statement, PreparedStatement 또는 CallableStatement를 사용하게 한다. 디폴트는 PREPARED 이다.
출처 - https://deeplify.dev/back-end/spring/select-key
2. 이슈 발생
저렇게 이해하고, 쉽게 작성할 수 있었는데요.
작성후에 신나게 디버깅한 결과..
엥..? 왜 값이 안담기지?? 했습니다.
위 쿼리대로면.. RFNM_BNAC_RGST_SRMB 값이 String이니까 resultType을 String으로 설정하면 제대로 리턴받겠지??
했지만..
insert 쿼리 실행하기 위해 xml 쿼리 및 xml 파일과 Mapping된 java 함수를 호출했더니..
→ Mybatis.mapperMethod 함수의 execute(). 여기를 통해 xml에 맵핑된 쿼리를 찾아 실행한 후, 결과를 리턴하는 과정으로 mybatis가 DB와 연동할 수 있습니다.
→ insert 쿼리를 호출하려했으니, switch를 통해 case INSERT:인 경우의 로직으로 들어가 insert SQL을 실행시키고, rowCountResult 함수를 통해 insert 결과를 리턴시키는 흐름으로 진행됩니다.
오키.. 알았담..
그럼 rowCountResult 함수 안으로 들어가보면..?
→ xml의 쿼리 설정값인 resultType을 어떤 것으로 설정했냐에 따라 method의 리턴 타입으로 정해지고, 이를 확인해서 그에 맞게 결과를 리턴해주는 함수인 듯 싶습니다..
→ 만약 void, 아무 값도 리턴해주지 않는다면 result=null 이군요..
즉,
⇒ 테스트 결과, xml SQL id와 맵핑하는 java class의 함수 return type에 따라 insert 쿼리의 결과 값이 바뀌는군요!!
근데 왜 이게 이슈야?? 하실겁니다.
→ 위에서 xml 쿼리의 insert 태그 안에 resultType=java.lang.String으로 했기 때문에, method.returnType 또한 String으로 되는 것 같습니다.
(스포하자면, 사실 아닙니다!)
→ default로 mybatis 에서는 insert SQL인 경우 Integer, Long, Boolean 으로 리턴해주기 때문에 이 외의 타입이 리턴타입으로 설정되있는 경우 Exception을 throw하게 만들어져있는데요..
→ 그래서, 해당 insert를 호출하는 함수의 리턴타입이 String이라 Exception을 던지는 것이었습니다..
결국..
위 insert 쿼리의 리턴 값을 selectKey를 통해 String 값을 결과값으로 리턴하기 때문에,
rowCountResult() 함수 내부에서 Exception을 던지고,
Exception으로 인해 insert가 실패하는 이슈로 번지게 되었습니다!
3. 이슈 해결(..?)
결론부터 말씀드리자면,
쉽게 해결했습니다..
위에서 작성했었던
insert 쿼리 실행하기 위해 xml 쿼리 및 xml 파일과 Mapping된 java 함수를 호출
여기서 Mapping된 java 함수를 호출 할 때
Mapping된 java 함수의 리턴 값을 void로 설정한다.
가 되겠습니다..
잊어버리고 java 함수를 캡쳐하지 못했었는데,
리턴 값이 String이다보니
String insertQuery(Object obj);
으로 구현했는데요.
이렇게하면 method의 resultType이 String으로 되어 Exception이 발생했던 것이었습니다.
위를
void inserQuery(Object obj);
으로 바꿔준다면 parameter로 쓰인 obj 객체 안에 keyProperty로 설정한 변수에 고스란히 Mapping될 것입니다!!
4. 끝줄
후..
사실 mybatis를 여러 프로젝트에 사용해봤었습니다만..
위 기능까지는 생각치 못했는데
최근에 다양한 관점으로 보는 것과
코드 리팩토링, 성능개선 을 생각하다보니
위 이슈와, 이슈 해결까지 할 수 있게 된 것 같습니다.
뭔가 뿌듯하고, 알아두면 좋을 것 같아서
노션에 기록해뒀다 포스팅하게 되었습니다.
덕분에 저도 selectKey에 대해 공부할 수 있게 되었습니다.
감사합니다.