mybatis야, 왜 insert하면 1을 꺼내주니?

허진혁·2023년 10월 23일
0

givemeticon 프로젝트

목록 보기
5/10

🤔 고민사항

MyBatis를 사용하여 프로젝트를 진행중인데 다음과 같은 문제를 만났으며 이와 관련되어 고민을 하고 있어요.

  • insert()를 성공하여 db에 값을 저장하였고, PK를 지정해두었는데, 왜 반환값이 항상 1일까?

  • insert()를 실행하면 반환해야 할 값이 무엇일까?

두 고민을 해결하기 위해 MyBatis 대한 간단한 설명과 INSERT 작업, 속성 그리고 옵션에 대해 공부하려고 해요.

마이바티스(MyBatis) 소개

MyBatis는 Java 프로그래밍 언어를 기반으로 한 데이터베이스 연동을 위한 오픈 소스 ORM(Object-Relational Mapping) 프레임워크에요. MyBatis SQL 기반의 데이터베이스 액세스를 단순화하고, 개발자가 SQL 쿼리를 직접 작성하고 관리할 수 있게 해주는 도구입니다. 또한 데이터베이스 연동 계층을 자동으로 생성하거나 복잡한 매핑을 수행하지 않으므로 개발자에게 높은 유연성을 제공합니다.

MyBatis 특징

MyBatis는 전통적인 ORM 프레임워크와 다른 점이 있습니다. 대표적인 ORM 프레임워크로는 Hibernate와 Java Persistence API (JPA)가 있습니다. MyBatis의 주요 특징은 다음과 같아요.

  • SQL 기반: MyBatis는 SQL 쿼리를 중심으로 동작하며, SQL을 직접 작성하고 관리할 수 있습니다.
  • 간결한 설정: 설정 파일을 통해 데이터베이스 연결 정보와 SQL을 간단하게 설정할 수 있습니다.
  • 자동 매핑: 데이터베이스 결과를 Java 객체에 자동으로 매핑할 수 있는 기능을 제공합니다.
  • 동적 SQL: 조건에 따라 SQL을 동적으로 생성할 수 있습니다.
  • 캐시 지원: 쿼리 결과를 캐시하여 성능을 향상시킬 수 있습니다.

MyBatis는 SQL을 중심으로 하는 간단한 설정과 유연한 SQL 작성을 통해 데이터베이스 액세스를 효율적으로 수행할 수 있으며, 많은 Java 어플리케이션에서 사용되고 있습니다.

Mybatis 쿼리별 반환타입

  • Select

    • 성공 : Select문에 해당하는 결과
    • 실패 : 에러
  • Insert

    • 성공 : Insert된 행의 개수 반환 (없다면 0)
    • 실패 : 에러
  • Update

    • 성공 : Update된 행의 개수 반환 (없다면 0)
    • 실패 : 에러 (성공적으로 실행되었지만 업데이트된 행이 없을 때 0을 반환. 이는 실패가 아님.)
  • delete

    • 성공 : Delete된 행의개수 반환 (없다면 0)
    • 실패 : 에러

이제 알 수 있어요. 내가 설정한 타입과 별개로 Mybatis 자체에서 insert가 성공하면 1을 반환해줘요.

Mybatis INSERT 작업

SQL 매퍼에서의 INSERT 작업 구현

Mybatis를 사용하여 데이터베이스에 새로운 레코드를 추가하려면 SQL 매퍼(XML 파일)에 해당 INSERT 쿼리를 정의해야 해요. 아래는 간단한 예제에요


<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
  INSERT INTO users (username, email, age)
  VALUES (#{username}, #{email}, #{age})
</insert>

위의 예제에서 save는 매퍼에서 사용할 고유한 ID이며, parameterType은 매개변수로 받을 객체의 타입이에요. SQL 쿼리는 <insert> 요소 안에 정의되며, #{}를 사용하여 파라미터 값을 바인딩해야 해요.

SELECTKey를 사용한 자동 생성된 키(primary key) 처리

데이터베이스에서 자동으로 생성된 키 (예: Primary Key)를 처리하려면 SELECTKey를 사용할 수도 있어요. 아래는 자동 생성된 키를 처리하는 예제에요

<insert id="save" parameterType="User">
  <selectKey keyProperty="id" order="AFTER" resultType="int">
    SELECT LAST_INSERT_ID()
  </selectKey>
  INSERT INTO users (username, email, age)
  VALUES (#{username}, #{email}, #{age})
</insert>

위의 예제에서 selectKey 요소를 사용하여 keyProperty에 자동 생성된 키를 설정하고 resultType으로 반환 타입을 설정해요. order 속성은 BEFORE 또는 AFTER 중 하나를 선택하여 자동 생성된 키를 언제 설정할지 지정할 수 있어요. BEFORE로 설정하면 INSERT 쿼리 실행 전에, AFTER로 설정하면 INSERT 쿼리 실행 후에 자동 생성된 키를 설정 반환해줘요.

🧐 위의 두 가지 방법 모두 실패 !!

PK가 자동생성 되기는 하지만 모두 삽입된 레코드의 id를 반납하는 것이 아니라 MyBatis의 insert가 성공되었다고 여전히 '1'을 반납했어요.

그래서 레코드를 삽입한 후의 값을 가져오는 방법을 선택했어요. 다음과 같은 순서를 생각했어요.

  1. dto를 domain으로 만든다.
  2. domain을 mapper를 통해 저장한다.
  3. domain에서 getId() 메서드를 리턴한다.

제가 작성한 코드는 다음과 같아요.

public int signUp(SignUpRequest request) {
        checkUserValidity(request);
        String encryptedPassword = passwordEncoder.encode(request.getPassword());

        User user = request.toEntity(encryptedPassword);
				// 포인트 적립, 유저 Role 변경 등의 작업...
        userMapper.save(user);

        return user.getId();
    }

드디어 원하던 결과가 나와요 !!!

MyBatis INSERT 속성과 옵션

useGeneratedKeys 속성

useGeneratedKeys 속성을 사용하면 데이터베이스가 자동으로 생성한 키(primary key)를 사용하여 자동으로 생성된 키를 처리할 수 있어요. 이 속성을 설정하면 자동 생성된 키를 사용하도록 데이터베이스 드라이버에 지시하게 되요. 아래는 useGeneratedKeys를 사용하는 예제에요:

<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
  INSERT INTO users (username, email, age)
  VALUES (#{username}, #{email}, #{age})
</insert>

useGeneratedKeystrue로 설정하면 keyProperty에 지정된 속성에 자동 생성된 키가 할당됩니다.

keyPropertykeyColumn 속성

keyProperty 속성은 자동 생성된 키를 매핑할 객체의 속성 이름을 지정해줘요. keyColumn은 데이터베이스에서 자동 생성된 키의 열 이름을 지정하게 되요. 이렇게 데이터베이스와 자바 객체 간의 매핑을 설정하는 거에요.

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id" keyColumn="user_id">
  INSERT INTO users (username, email, age)
  VALUES (#{username}, #{email}, #{age})
</insert>

위의 예제에서 keyProperty는 자바 객체(User)의 속성 이름을, keyColumn은 데이터베이스에서 사용하는 열 이름을 지정하면 되요.

🥸 마지막 고민

INSERT 작업의 반환 타입을 선택하는 것은 어떻게 하는 것이 좋을까요?

두 가지 상황을 가정해 보았어요.

  1. 단순한 회원가입으로 회원가입의 성공과 관련된 로직이 없을 때

이런 경우 리턴 타입을 void로 해도 상관 없을 것 같아요. 회원가입한 유저의 id를 사용할 필요가 없으면 굳이 id를 반환할 필요가 없어요.

  1. 데이터를 생성 한 후 추가적인 로직에 유저가 필요할 때

예를 들어, 계좌를 생성하면 기존 유저가 갖고 있는 필드 account_id에 생성된 계좌 id를 넣어야 한다고 가정해봐요. 그렇다면 계좌를 생성하면서 자동 증가한 id를 유저의 account_id 필드 값에 넣어주어야 해요. 이런 경우는 return type을 int 혹은 long 으로 해두어 가지고 와야 해요.

즉, 이에 대한 결론은 없으며 요구사항에 맞게 선택해야 해요.

이것으로 저의 고민을 마칠게요.
감사합니다 !! 👋

참고자료

mybatis에서 selectKey 사용법
mybatis 공식문서
Get the id of last inserted record in mybatis

profile
Don't ever say it's over if I'm breathing

0개의 댓글