[Mybatis] insert한 컬럼의 PK값을 바로 리턴받기(auto generated)

Ogu·2024년 3월 24일
2

상황

쇼핑몰 프로젝트의 v1을 Mybatis로 구현하는 과정에서 저는 Product의 도메인을 맡았습니다.

그 중, 상품 생성시 service계층에서 PK값을 바로 리턴받아 Controller에서 헤더에 URI값에 포함시켜 넘겨주고 싶었습니다.
ex) /v1/products/13

하지만 왜인지 null을 return받고 있었습니다. (이것은 의도와는 별개로 int가 아닌 Long으로 Return값을 받으려고 했기 때문)

먼저 생각난 방법은 다시 DB에서 PK값을 SELECT 조회해야 하나? 싶었지만 비효율적이라는 생각이 들어 구글링을 시작했습니다.

MyBatis의 쿼리 기본 return값

Mybatis는 insert, update, delete시 int를 반환합니다.

Mybatis에서는 기본적으로 쿼리가 돌고 나면 업데이트 한 행의 개수를 리턴한다고 합니다.

MyBatis성공 시 return값유효성 검사
INSERT1( 다중 insert도 1) null 일 때 cnt = 1로 변경후 cnt == 1
UPDATEupdate된 행의 갯수 반환(없으면 0)반환값 cnt로 받고 cnt > 0
DELETEdelete된 행의 갯수(없으면 0)반환값 cnt로 받고 cnt > 0

참고

해결 방법

INSERT 후 PK값을 return받기

하지만 PK를 바로 다른 테이블의 FK로 사용하고 싶은 등 PK를 바로 받아서 사용해야 할 상황들이 분명 있습니다.

MyBatis는 두가지 방법을 제공합니다.
1. selectKey
2. useGeneratedKeys - (DB가 Auto Increment를 지원시)

1. selectKey

보통 INSERT 쿼리문 내부에 <selectKey> 블럭을 입력해서 사용합니다.

<insert id="..." parameterClass="..." >

	INSERT INTO...

    <selectKey keyProperty="product_id" resultType="Long" order="AFTER" >
        SELECT LAST_INSERT_ID()
    </selectKey>
</insert>

속성값들은 아래와 같은 의미를 가지고 있습니다.

  • result type : 반환할 pk의 타입
  • order : SELECT LAST_INSERT_ID()를 언제 실행할지 결정(감싼 쿼리문의 전, 후)
  • keyProperty : 반환받을 PK가 매핑될 속성을 지정 (반환받고 싶은 column)

2. useGeneratedKeys

id가 autoincrement인 PK일 경우, insert 된 행의 pk값을 가져옵니다.
관련 속성은 <insert></insert> 안에 작성합니다.

<insert id="save"
            parameterType="org.store.clothstar.product.domain.Product"
            useGeneratedKeys="true" keyProperty="productId" keyColumn="product_id">
        INSERT INTO product(product_id, member_id, category_id, name, price, stock, status, created_at)
        VALUES (#{productId}, #{sellerId}, #{categoryId}, #{name}, #{price}, #{stock}, #{status}, #{createdAt})
</insert>
  • useGeneratedKeys: 자동 생성된 키를 반환 받을 것인지(default = false)
  • keyProperty: MyBatis에서 생성된 키를 어느 프로퍼티에 설정할지를 결정하는 속성 (prrameterType의 매핑할 변수명)
  • keyColumn = [PK 컬럼명]

해당 insert의 반환 int값은 당연히 1로 변동되지 않습니다.
따라서 아래와 같이 하면 당연히 1이 나오겠죠.

int idx = productRepository.save(product);
System.out.println("idx : " + idx)

따라서 아래와 같이 작성해야 PK값을 받을 수 있습니다.

productRepository.save(product);
System.out.println("idx : " + product.get("idx"))

전체 수정 코드

우선 product 도메인 클래스는 다음과 같습니다.

@Getter
@Builder
public class Product {
    private Long productId;
    private Long sellerId;
    private Long categoryId;
    private String name;
    private int price;
    private int stock;
    private ProductStatus status;
    private LocalDateTime createdAt;
    private LocalDateTime modifiedAt;
    private LocalDateTime deletedAt;
}

productMapper.xml

<insert id="save"
            parameterType="org.store.clothstar.product.domain.Product"
            useGeneratedKeys="true" keyProperty="productId" keyColumn="product_id">
        INSERT INTO product(product_id, member_id, category_id, name, price, stock, status, created_at)
        VALUES (#{productId}, #{sellerId}, #{categoryId}, #{name}, #{price}, #{stock}, #{status}, #{createdAt})
    </insert>

productRepository

Mapper클래스입니다.

@Mapper
public interface ProductRepository {
    List<Product> selectAllProductsNotDeleted();
    Product selectByProductId(Long productId);
    int save(Product product);
}

productService

@Transactional
    public Long createProduct(CreateProductRequest createProductRequest) {
        Product product = createProductRequest.toProduct();
        productRepository.save(product);
        return product.getProductId();
    }

productController

@PostMapping
    public ResponseEntity<Long> createProduct(@Validated @RequestBody CreateProductRequest createProductRequest){
        Long productId = productService.createProduct(createProductRequest);
        return ResponseEntity.created(URI.create("/v1/products/" + productId)).build();
    }

결과 확인

다음과 같이 정상적으로 id값이 잘 들어왔습니다.

DB조회로 올바른 pk값이 넘어왔는지 확인해보겠습니다.

참고

profile
Hello! I am Ogu, a developer who loves learning and sharing! 🐤🐤 <br> こんにちは!学ぶことと共有することが好きな開発者のOguです!🐤

0개의 댓글