iBatis에서 myBatis로 넘어오면서 selectKey에서 last_insert_id 를 가져오는 방식에 변화가 생겼습니다.

기존 iBatis 방식으로 넘겨받으면 1만 넘어오는데, 이는 increase된 selectKey 가 아닌 inserted row count 입니다.
myBatis에서는 insert시 전달받은 parameter class에 keyProperty로 정의한 값 또는 object로 자동으로 inject 됩니다.

이전 iBatis에서도 그랬는지는 기억이 가물가물하네요. 암튼 경험한 부분을 명시합니다.

 

간단한 예를 들어서 설명합니다.

 

<insert id="insertUser" parameterMap="map">
    INSERT INTO USER (
      NAME, ID, PW 

    )VALUES(
        #{NAME}, #{ID}, #{PW}
    )
<selectKey keyProperty="USER_IDX" resultClass="java.lang.Integer">
    SELECT LAST_INSERT_ID()
</selectKey>
</insert>

 

int insertRowCnt = userMapper.insertUser(map);
int userIdx = map.get("USER_IDX");


다시말해서 위와 같은 방법으로 가져와야 합니다.

 

이전 iBatis에서는 아래와 같이 요청해도 last_insert_id를 리턴해줬던것 같았는데...

int userIdx = userMapper.insertUser(map);

 

만약 map 대신 POJO클래스(UserObject 같은)를 사용한다면 pojo 형태로 keyProperty를 정의합니다.

 

<insert id="insertUser" parameterType="UserObject">
INSERT INTO USER (
  NAME,  ID, PW
)VALUES(
  #{name},  #{id}, #{pw}
)
<selectKey keyProperty="userIdx" resultClass="java.lang.Integer">
    SELECT LAST_INSERT_ID()
</selectKey>
</insert>


int insertRowCnt = userMapper.insertUser(userObject);
int userIdx = userObject.getUserIdx(); <==

 

결론 : 변수로 넘겨준 class에 자동으로 injection된다는 것.

차라리 iBatis를  경험하지 않았더라면 이해가 더 빨랐을 수도 있을것 같습니다. 그저 비슷하려니 하는 막연함이 부른 삽질.

아니면 이전부터 있었던 동일한  기능이었는데, 제대로 알지 못하고 사용했는지도 모르겠습니다. 집에가서 iBatis 책을 찾아봐야겠습니다.

 

이글을 읽는 분들은 부디 저처럼 삽질하지 마시길 바랍니다. 화이팅~

 

 




selectKey사용시 주의할점.

 

그동안 유용하게 사용하던 iBatis의 selectKey가 어느 순간 가져오지 못하는 경우가 발생했다.

insert 후 LAST_INSERT_ID()를 keyProperty에 담아 가져오게 되는데, 수정후에 가져오지 못하는것 경우가 발생했다.

 

이전과 달라진 점이라고 한다면 where절에 들어가는 조건이 하나 줄어 굳이 hashmap에 담지 않고

integer로 가져와도 되기에 parameterClass를 hashmap에서 int로 변경.

<insert id="insertUserInfo" parameterClass="KwdGrpClass or hashmap">

insert into kwd_grp ( name, kwd_idx, type )

values(#name#, #kwd_idx#,#type#)

<selectKey keyProperty="kwd_grp_idx" resultClass="java.lang.Integer">
   SELECT LAST_INSERT_ID()

</selectKey>

</insert>

 

selectKey는 거의 insert후에 자동증가한 값을 가져오기 위해 사용하는 방법인데,

대부분의 insert시 parameterClass로 hashmap에다 Object Class를 사용하게 되므로

아무 생각없이 selectKey의 keyProperty에 대한 존재감을 잊게되버리는 경우가 발생한다.

 

하지만 종종 insert문을 select Insert로 사용하게 되는 경우 사용시 주의해야 한다.

ex> insert into kwd_grp ( name, kwd_idx, type )

select  name, kwd_idx, type

from kwd

where kwd_idx = #kwd_idx#

 

위의 예는 억지로 만든거라서 좀 그렇긴 하지만 위의 경우 parameterClass는 string이 될것이다. 그러므로 selectKey를 사용하여 마지막 추가된 idx값(대부분 integer형태)을 가져오려면 keyProperty를 지정하지 않아야 한다. select insert같은 쿼리를 이용해 값을 추가할 때 hasmap이나 object Class 가 아닌 int, string을 parameterClass로 사용하기도 하는데, 이때는 keyProperty를 지정할수 없게 된다.

 조금만 생각해보면 당연한 걸수도 있는데, 그저 습관적으로 사용하다보면 그게 당연한걸로 인식되게 되고 그러다 보면 원리를 모른채 코딩을 하게 되곤한다.

 특히 쿼리나 메소드, 클래스등의 튜닝이나 Refactoring시 함께 변경해 줘야 할것이다.



오라클을 사용하여 프로그램할땐 Sequence의 nextVal을 이용하여 insert하고 해당 값으로 관련된 다른 테이블에 사용하곤 했었는데...
MySQL이나 MSSQL은 auto_increment, identity를 이용하여 디비 내부적으로 자동증가하여 그 부분을 어떻게 처리할까 고민하곤 했었다. 내부적으로 SCOPE_IDENTITY(), LAST_INSERT_ID() 함수를 이용하여 가져올수 있다는 것을 나중에서야 알았따.

이제 본론으로.... iBatis를 이용할때 이부분 더 좋게 지원해준다.
<selectKey>를 이용하면 프로그램쪽에서 한번더 select하는 번거로움을 덜어주고 성능면에서도 조금은 도움을 줄것으로 생각된다.

* 키값이 자동증가인 경우 ...
<insert id="insertId">
  insert into MEMBER (MBR_ID, NAME) values
    ( #mbrId#, #name# )
  <selectKey keyProperty="memberSeq" resultClass="int">
    SELECT  LAST_INSERT_ID()
  </selectKey>
</insert>
Integer result = sqlMap.insert("insertId", member);

이렇게 처리하면 result에 마지막으로 추가된 키값이 담기게 된다.

* 키값이 자동증가가 아닌경우...

<insert id="insertId">
  <selectKey keyProperty="memberSeq" resultClass="int">
    SELECT MAX(MBR_SEQ) + 1 FROM MEMBER
  </selectKey>
  insert into MEMBER (MBR_SEQ, MBR_ID, NAME) values
    ( #memberSeq#, #mbrId#, #name# )
  <selectKey keyProperty="memberSeq" resultClass="int">
    SELECT  LAST_INSERT_ID()
  </selectKey>
</insert>

이와 같이 처리하면 끝.