-
MyBatis 관련 삽질 모음database 2022. 2. 28. 06:47
mybatis을 사용하면서 겪은 에러나 알게 된 내용들을 여기에 정리해두고자 한다. 앞으로 새로운 내용이 생길 때마다 여기에 업데이트하게 될 것 같다.ORA-00928: 누락된 SELECT 키워드
이 에러는 정말 다양한 상황에서 발생할 수 있는 것 같다. 이 에러로 검색해보면 원인이 참 다양하다. 다양한 상황에서 발생할 수 있는만큼, 살짝은 불친절한 메시지가 아닌가 한다. 이 에러가 떴다면 실제로 실행된 쿼리에서 실수를 샅샅이 찾아야 한다. 사실 간단한 SQL을 작성해서 바로 실행해본다면 실수를 금방 깨달을 수 있지만, 길고 복잡한 SQL 구문이나 mybatis mapper에 작성한 코드라면 에러를 바로 눈치채기 쉽지 않다. SQL 문법 실수를 한 것이 아닌지 가장 먼저 의심해보고, 동적 쿼리인 경우 실제로 실행된 쿼리를 확인해봐야 한다. 내 경우에는 SQL문에는 문제가 없었지만, 동적으로 들어가게 되는 데이터가 실수로 null을 집어넣고 있어서 완성된 쿼리가 이상한 모양이었다.
Oracle java.sql.SQLException: 부적합한 열 유형
이 예외를 검색했을 때 딱히 도움되는 답을 얻지 못했다. 도대체 뭘까...하고 쳐다보다가 내가 코멘트 처리를 잘못 했음을 깨달았다. mapper.xml에서
command
+/
로 코멘트 처리한 부분이 문제였다. 이렇게 하면--
가 앞에 붙으면서 회색으로 변한다. 그래서 에디터 상에서 눈으로 볼 때는 코멘트 처리가 잘 된 것처럼 보였지만, 실제로는 유효한 코드로 읽어들인 것이다. xml에서의 코멘트는<!--
와-->
로 감싸져 있어야 한다.There is no getter for property named XXX
혹시 mapper 파일에서 파라미터를 잘 못 읽는다면, 해당 파라미터 앞에
@Param
애너테이션을 붙이면 해결된다.Invalid bound statement (not found)
이거 오류를 검색하면 메서드명 실수나 경로 이야기가 많이 나왔는데, 내 경우에는
<mapper namespace="blah">
에 넣은 경로가 잘못 되어 있었다. 다른 컴포넌트에 있는 xml 파일을 복붙해오다 보니 이 부분도 바뀌어야한다는 것을 깜빡했다.bulk merge into 올바르게 사용하기
oracle에서 upsert(자료가 없으면 insert, 이미 존재하면 update)를 할 때에는
merge into
구문을 사용한다. 내 경우에는 이걸 bulk로 진행하고 싶어서 mybatis의<foreach>
기능을 사용했는데, 이 과정에서 삽질을 좀 많이 했다.올바른 사용 방법은 이 블로그에서 친절하게 설명이 되어 있다.
<foreach>
가 의외의 위치에 있다는 생각이 들 수 있지만,merge into
문법을 잘 이해하고 나면 이상할 것이 없다.using
을 이용해서 가짜 테이블(T1)을 만들고, 그 결과를on
에 사용한다.on
은 매칭되는 row가 있는지 확인하기 위한 조건이다. 만약methodName
메서드의 파라미터만을 이용해서on
조건을 만들 수 있다면using dual on
의 문법을 사용하면 된다.<update id="methodName" parameterType="java.util.List"> MERGE INTO SOME_TABLE R1 USING ( <foreach collection="parameterName" item="item" open="" close="" index="index" separator="UNION"> SELECT #{item.property1} as property1, #{item.property2} as property2, #{item.property3} as property3, FROM DUAL </foreach> ) T1 ON (R1.matching_column1 = T1.property1) WHEN MATCHED THEN UPDATE SET R1.matching_column1 = T1.property1, R1.matching_column2 = T1.property2, R1.matching_column3 = T1.property3 WHEN NOT MATCHED THEN INSERT ( matching_column1, matching_column2, matching_column3 ) VALUES ( T1.property1, T1.property2, T1.property3 ) </update>
내가 한 실수는
<foreach>
에 있었다.<foreach>
의 각 요소를 살펴보자면 다음과 같다.- collection: 무언가 순회할 수 있는 데이터를 파라미터로 넘길텐데, 그 이름을 적으면 된다.
- item: 여기에 부여한 이름으로 #{item}처럼 접근할 수 있다.
- index: 여기에 부여한 이름으로 #{index}처럼 접근할 수 있다.
- open, close, separator:
<foreach>XXX</foreach>
처럼 되어 있다면 XXX가 반복될텐데, 그 시작과 끝, 그리고 각 요소 사이에 어떤 코드를 넣을지를 명시하는 것이다. 위 코드의 경우 각 select문의 결과를 union 하고 싶기 때문에 separator에 union을 명시했다. 위 코드에서는 open과 close에 공백이나 괄호를 넣어도 완성되는 SQL문에는 문제가 없을 것이다.
이건 다른 이야기이지만, mybatis를 사용하면 이렇게 사람이 실수할 구석이 많다는 것 자체가 별로인 것 같다. 실수 하나 할 때마다 생산성이 갉아먹히는 기분이다. 실수를 작성하면서 바로 알 수 있도록 IDE에서 기능이 지원되면 좋을 것 같고(이런 찾아보면 플러그인이 분명 있을 것 같다), mybatis를 사용한다면 더 쉽고 빠르게 테스트를 해볼 수 있는 방법을 찾아야할 것 같다(지금은 스프링 테스트를 하거나 서버를 아예 실행해보는 데 더 좋은 방법이 없을까). 만약 ORM을 사용할 수 있는 기능이라면 ORM을 적극적으로 활용하는 것이 좋을 것 같다. 만약 반드시 SQL을 사용해야 한다면 mybatis 말고 더 편리한 SQL mapper 기술을 사용하는 것도 좋을 것 같다.'database' 카테고리의 다른 글
쿼리 튜닝하기 (0) 2022.04.18 많이 들어본 DB 기술 정리 (0) 2022.01.16