본문 바로가기
Framework/MyBatis

<selectKey> : insert + select를 한번에

by 예스p 2023. 4. 20.

<selectKey>

Mybatis에서 유용하게 활용할 수 있는 태그인

<selectKey>에 대해서 알아보자


<selectKey>란?

  • DML문(insert,update,delete)에 쿼리문(select)을 활용해야하는 경우
    >> 별도의 쿼리를 작성하지 않고 하나의 메소드에 일괄처리할 수 있다.
  • 여러 테이블에 동일한 시퀀스를 insert하는 경우에 주로 사용된다.
    >> 발생한 시퀀스를 조회하는 currval은 동일한 트랜젝션에서만 쓸 수 있다.
      스프링은 하나의 SQL문이 실행될따마다 자동 커밋을 하고 있어 일반적 상황에서 currval은 쓸 수 없다.
      <selectKey>는 이러한 한계를 우회할 수 있는 지름길을 제공한다.
  • 공식문서에서 보기
    : 원어, 한국어 번역

 

 

예제1 : 개념다지기

  • 하단의 예제는 selectKey 가 가장 많이 활용되는 insert 메소드 이다.
    예제태그의 흐름을 보면 다음과 같다.
    1. insert 태그 호출 당시에 Author 객체가 파라미터로 전달되었을 것
    2. selectKey가 실행되며 select문의 결과값(하기예시는 랜덤값)이 Author의 id 속성에 저장된다.
    3. insert문이 실행되며 Author가 가진 속성값들이 테이블에 저장된다.
      이때, #{id}에 selectKey가 조회한 값이 담겨있다.
<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    SELECT CAST(dbms_random.value(0, 999999) AS INT) a FROM DUAL <!-- 오라클 -->
  </selectKey>
  insert into Author
    (id, username, password)
  values
    (#{id}, #{username}, #{password})
</insert>

 

 

selectKey가 가질 수 있는 속성

  • keyProperty
    - <selectKey> 의 결과값이 위치할 자바객체의 속성(프로퍼티). 
      (상위 태그의 파라미터로 전달받은 객체, setter메소드가 있어야 한다.)
    - 결과값의 칼럼이 여러개라면 콤마로 구분하여 나열할 수 있다.
  • keyColumn
    - 프로퍼티에 매칭시킬 조회된 결과의 칼럼.
    - PostgreSQL의 경우, 매핑 칼럼이 첫번째 칼럼이 아니라면 필수로 입력해야한다.
    - 콤마로 구분하여 여러개를 나열할 수 있다.
  • resultType
    - 결과에 대해서 매핑받을 타입(생략가능).
    - Mybatis의 CRUD방식 포스팅에서 입력규칙 및 기등록된 별칭을 확인할 수 있다.
    - 칼럼이 여러개라면, Map 이나 객체(매칭되는 프로퍼티를 가진)를 사용해야 한다.
  • order
    - selectKey구문은 DML문 위 아래 어디든 위치할 수 있고 order 속성으로 실행 순서를 정의
    - BEFORE :
      기본값. selectKey조회를 한 후 insert구문을 실행
    - AFTER : 
       insert 구문을 실행한 뒤 selectKey 구문을 실행
       오라클과 같이 insert구문 안에 내장된 시퀀스가 있을경우 사용한다.

 

 

예제2 : method를 AFTER로 설정하는 경우

  • selectKey의 속성 중 order="AFTER"로 설정했다면 돌려받은 데이터를 다른계층(Dao, Service,..)에서 활용할 수 있게끔 코드를 작성해야 한다.
    예제태그의 흐름을 보면 다음과 같다.
    1. Post 테이블 insert 구문에서 시퀀스를 발생시킨다.
    2. selectKey를 이용해서 Post객체의 postSeq 속성에 현재 발생한 시퀀스를 저장한다. 
    3. 해당 코드가 실행된 이후라면 스프링컨테이너에 있는 Post객체에는 새로운 정보가 담겨있어서 어떤 레이어에서도 접근 가능하다.
    4. Attachment에 데이터를 세팅한 후 Attachment 테이블에 insert하러 간다.
  • selectKey의 order를 어떻게 설정하든 DML문의 리턴값은 처리된 행수라는 점을 헷갈리지 말자.
//Dao단 코드
public void insertPost(Post post) {
  sqlSession.insert("insertPost", post);
}
public void insertAttachment(Long postId, Attachment attachment) {
  attachment.setPostSeq(post.getPostSeq());
  sqlSession.insert("insertAttachment", attachment);
}
<insert id="insertPost" parameterType="Post">
  INSERT INTO post (post_seq, title, content) VALUES (nextval(post_seq), #{title}, #{content})
  <selectKey resultType="int" keyProperty="postSeq" order="AFTER">
    SELECT currval('post_seq') <!-- PostgreSQL --> 
  </selectKey>
</insert>

<insert id="insertAttachment" parameterType="Attachment">
  INSERT INTO attachment (post_seq, filename) VALUES (#{postSeq}, #{filename})
</insert>

 

 

대안 : select 메소드로 insert하기

  • 몇몇 DBMS에서는 DML문을 처리하고 나서 결과를 돌려받을 수 있다.
    - PostgreSQL, MariaDB : returning 구문
    - MS SQL : output 구문 
  • 이러한 경우 select문 메소드만으로도 insert와 select문을 합친 효과를 가질 수 있다.
<select id="insertAndGetAuthor" resultType="domain.blog.Author"
      affectData="true" flushCache="true">
  insert into Author (username, password)
  values (#{username}, #{password})
  returning id, username, password
</select>

 

 

 

 

 

 

댓글