[ibatis] 자동생성키 <selectKey> 사용 정리 (채번, Sequence)

2019. 10. 7. 17:13 Java 관련/MyBatis, iBatis

자동생성키 <selectKey> 사용 (채번, Sequence) 



대부분의 RDBMS 시스템은 Sequence 와 같은 채번 자동 생성을 지원한다.

개발을 하다보면 종종 자동생성된 Sequence 값을 가져와서 사용해야 하는 경우가 있다.

이런 경우에 <selectKey>를 사용한다.


<selectKey>는 ibatis의 statement 타입 중 <insert> 타입에만 사용할 수 있는 하위 요소이다.


[Java Web/MyBatis, iBatis] - [ibatis] sqlMap XML 파일의 기본 구조



<insert> 문 아래에 사용함으로써, 자동생성된 키 값을 가져오며, 이 값을 반환하여 활용할 수 있다.


* <selectKey> 사용 기본 예제



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">


<sqlMap namespace="Product">
        
<insert id="insertProduct-Oracle" parameterClass="com.domain.Product">
   <selectKey keyProperty="id" resultClass="int">
       SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
   </selectKey>

    INSERT INTO PRODUCT(PRD_ID, PRD_DESCRIPTION)
    VALUES(#id#, #description#)
    
</insert>
    
</sqlMap>


위의 예제를 보면 <insert> 문 아래에 <selectKey>가 있다.

<selectKey> 아래에 있는 쿼리를 먼저 실행시키고 결과 값(resultClass)을 ID 라는 변수(keyProperty)로 parameterClass인 Product의 id 변수에 대입된다.


 SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL


이것은 STOCKIDSEQUENCE 라는 이름의 SEQUENCE가 DB에 등록되어 있으면, 다음 채번을 얻어온 후 Product 객체의 int 형 "id" 변수에 담고,

아래 INSERT 쿼리에 #id#에 이 값이 대응되어, 데이터가 Insert 된다.

(keyProperty 를 지정하지 않으면 Alias 로 지정한 컬럼명으로 <selectKey> 결과 값이 대응된다.

정확한 값이 대응되기 위해서는 keyProperty 값 == Product 변수 이름 == Table 컬럼 명 이 되야 한다.)



* <selectKey>의 활용


위의 예제를 보면 <selectKey>가 직접적으로 Sequence 채번을 지원하는 것은 아니다. 단지 <selectKey> 에 있는 쿼리 문을 실행하고 결과를 변수에 담아 insert 쿼리에 활용할 수 있도록 한 것이다.

단지 insert 만을 위한 이런 이유라면, 굳이 <selectKey> 요소를 사용하지 않고, 아래와 같은 방법으로 작성할 수 있다.



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"
    "http://www.ibatis.com/dtd/sql-map-2.dtd">


<sqlMap namespace="Product">
        
<insert id="insertProduct-Oracle" parameterClass="com.domain.Product">
   
    INSERT INTO PRODUCT(PRD_ID, PRD_DESCRIPTION)
    VALUES(STOCKIDSEQUENCE.NEXTVAL, #description#)
    
</insert>
    
</sqlMap>


위와 같이 Sequence 다음 채번을 바로 #id#가 들어갈 자리에 넣어주면 된다.


하지만 <selectKey> 를 사용하는 이유는 생성된 값을 다음에 활용하기 위하여 저장한다는데에 있다.

개발을 하다보면 위와 같이 PRODUCT 테이블에 데이터를 insert 한 후, 참조 또는 자식 테이블에 연속으로 데이터를 insert 해야 되는 경우가 있고 이때 생성된 Sequence 값을 필요로 할 수 있다. 이를 위해 <selectKey>를 사용한다.


위에서 언급했듯이, <selectKey>를 사용하여 얻은 키 값은 keyProperty 와 대응되는 parameterClass의 변수에 저장되기 때문에, 다음에 이 값을 활용할 수 있는 것이다.

이런 경우에 <selectKey>를 활용하면 된다.



* 멀티 쓰레드에서 발생할 수 있는 문제의 해결을 위한 사용


Sequence를 통해 생성한 키 값을 필요로 할때, 여러가지 방법으로 가져온다.


보통은,

 

INSERT INTO PRODUCT(PRD_ID, PRD_DESCRIPTION)

           VALUES(STOCKIDSEQUENCE.NEXTVAL, ? ) 


Sequence 다음 값으로 채번한 값을 키 값으로 Insert 한 후에


SELECT STOCKIDSEQUENCE.CURRVAL FROM DUAL


또는 , 


SELECT MAX(PRD_ID) FROM PRODUCT 


와 같은 방법으로 입력된 키 값을 가져온다.



하지만, 이 방식은 멀티쓰레드 환경에서 위험이 발생할 여지가 있다.


예를 들어 2개의 쓰레드가 동시에 INSERT 문을 실행하면, 


<1번 채번 INSERT> <2번 채번 INSERT> <1번 채번 조회 : 값 2번 채번 값> <2번 채번 조회 : 값 2번 채번 값>  의 순서로 진행되어 1번 채번 INSERT 후 가져온 해당 키 값이 2번 채번 값으로 가져와 오류를 발생시킬 수 있다.


여기서 <selectKey> 를 사용하면, 채번한 값을 객체에 저장한 후 그 값을 INSERT에 사용하므로, 멀티쓰레드 상황에서도 안전하게 값일 가져올 수 있다.



출처: https://hyeonstorage.tistory.com/280?category=549765 [개발이 하고 싶어요]