쇼핑몰 프로젝트의 v1을 Mybatis로 구현하는 과정에서 저는 Product의 도메인을 맡았습니다.
그 중, 상품 생성시 service계층에서 PK값을 바로 리턴받아 Controller에서 헤더에 URI값에 포함시켜 넘겨주고 싶었습니다.
ex) /v1/products/13
하지만 왜인지 null을 return받고 있었습니다. (이것은 의도와는 별개로 int가 아닌 Long으로 Return값을 받으려고 했기 때문)
먼저 생각난 방법은 다시 DB에서 PK값을 SELECT 조회해야 하나? 싶었지만 비효율적이라는 생각이 들어 구글링을 시작했습니다.
Mybatis는 insert, update, delete시 int를 반환합니다.
Mybatis에서는 기본적으로 쿼리가 돌고 나면 업데이트 한 행의 개수를 리턴한다고 합니다.
MyBatis | 성공 시 return값 | 유효성 검사 |
---|---|---|
INSERT | 1 | ( 다중 insert도 1) null 일 때 cnt = 1로 변경후 cnt == 1 |
UPDATE | update된 행의 갯수 반환(없으면 0) | 반환값 cnt로 받고 cnt > 0 |
DELETE | delete된 행의 갯수(없으면 0) | 반환값 cnt로 받고 cnt > 0 |
참고
하지만 PK를 바로 다른 테이블의 FK로 사용하고 싶은 등 PK를 바로 받아서 사용해야 할 상황들이 분명 있습니다.
MyBatis는 두가지 방법을 제공합니다.
1. selectKey
2. useGeneratedKeys - (DB가 Auto Increment를 지원시)
보통 INSERT 쿼리문 내부에 <selectKey>
블럭을 입력해서 사용합니다.
<insert id="..." parameterClass="..." >
INSERT INTO...
<selectKey keyProperty="product_id" resultType="Long" order="AFTER" >
SELECT LAST_INSERT_ID()
</selectKey>
</insert>
속성값들은 아래와 같은 의미를 가지고 있습니다.
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>
해당 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;
}
<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>
Mapper클래스입니다.
@Mapper
public interface ProductRepository {
List<Product> selectAllProductsNotDeleted();
Product selectByProductId(Long productId);
int save(Product product);
}
@Transactional
public Long createProduct(CreateProductRequest createProductRequest) {
Product product = createProductRequest.toProduct();
productRepository.save(product);
return product.getProductId();
}
@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값이 넘어왔는지 확인해보겠습니다.