ํ์ฌ ์งํ์ค์ธ Mybatis ํ๋ก์ ํธ์์ Mybatis ๋งคํ ๊ฐ์ฒด (resultType ๋๋ resultMap)์ @NoArgsConstructor
์ ๋ถ์ฌ ๊ฐ์ ธ์ค๋ ค ํ๋๋ฐ ์๊พธ ํน์ ๋ณ์์์ null์ผ๋ก ๋งคํ๋๊ณ ์์ง ๋ชปํ ์ํ์์ต๋๋ค.
๊ทธ๋์ ํ์์๋ @NoArgsConstructor
์ ์ฌ์ฉํ์ง ์๊ณ @Getter
์ @Builder
๋ง ์ฌ์ฉํ์ฌ ๋งคํํ๊ณ , ์ ์์ ์ผ๋ก ์ ๋ถ ๋ค ์ ๋งคํ์ด ๋์์ต๋๋ค.
ํ์ง๋ง Mybatis๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ธฐ๋ณธ์์ฑ์๋ฅผ ํตํด ๊ฐ์ฒด๋ฅผ ๋งคํํ๋ค๊ณ ๋ชจ๋ ๊ธฐ์ ๋ธ๋ก๊ทธ์์ ๋งํ๊ณ ์์ด์ ์๋ฌธ์ด ์๊ฒผ์ต๋๋ค.
@Getter
์ @Builder
๋ง ์ฌ์ฉํด๋ ์ ์์ ์ผ๋ก ๋งคํ๋์ง?ํ์ ์๋ฌธ์ ์ด ์์์ง๋ง ๊ทธ๋ฅ ๊ธฐ๋ฅ ๊ฐ๋ฐ์ ๋ฐ๋น ์๋ฌธ์ ์ ๋ฌตํ๋๊ณ ์๋ค๊ฐ, ๋ ๊ด๋ จํ์ฌ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ์ด๋ฒ์๋ ํ์คํ ๋ถ์ํ๊ณ ํด๊ฒฐํด ๋ณด๊ธฐ๋ก ํ์ต๋๋ค.
๋ฌธ์ ๊ฐ ๋ฐ์ํ ์์ ์ Mybatis์์ 1:N ๊ด๊ณ๋ฅผ List๋ก ํฌํจํ์ฌ ๊ฐ์ ธ์ค๋ ค ํ ๋ ๋ฐ์ํ์ต๋๋ค.
ORM์ธ JPA๋ฅผ ์ฌ์ฉํ์ ๋๋ ์์ฝ๊ฒ @OneToMany
์ @ManyToOne
์ด๋
ธํ
์ด์
์ ์ฌ์ฉํ์ฌ ๊ฐ๋จํ๊ฒ ๊ฐ์ ธ์ฌ ์ ์์์ต๋๋ค.
ํ์ง๋ง Mybatis๋ ์ด๋ฌํ 1:N๊ด๊ณ๋ฅผ join sql์ผ๋ก ๊ฐ์ ธ์ค๊ธฐ ์ํด์๋ resultMap
๊ณผ collection
์ ์ฌ์ฉํด์ผ ํ์ต๋๋ค.
์ฆ, ๊ฐ์ฒด ์์ ๊ฐ์ฒด๋ฅผ ๋งคํํด์ผ ํ์ต๋๋ค.
๋ฐ๋ผ์ ์ด๋ฒ ํฌ์คํ ์์๋
1. ๊ธฐ๋ณธ์์ฑ์ (@NoArgsArgument
)๋ฅผ ์ฌ์ฉํ์ฌ ๋งคํํ ๊ฒฝ์ฐ ์ ํน์ Column์ null๋ก ๋งคํ๋๋๊ฐ
2. ์ด๋ป๊ฒ ํ๋ฉด ๊ธฐ๋ณธ์์ฑ์๋ฅผ ํตํด ๋งคํํ ๋ ์ ์์ ์ผ๋ก ๋งคํํ ์ ์๋๊ฐ
์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
@NoArgsArgument
๋ฅผ ์ฌ์ฉํ์ฌ ๋งคํํ ๊ฒฝ์ฐ, ์ ํน์ Column์ null๋ก ๋งคํ์ด ์ ๋๋ก ์ด๋ฃจ์ด์ง์ง ์์๊น?๋ค์๊ณผ ๊ฐ์ด, ๊ธฐ๋ณธ์์ฑ์๋ฅผ ๊ฐ๊ณ ์๋ ๊ฐ์ฒด๋ฅผ ํตํด ๋งคํ์ ํ๋ ค ํ์ง๋ง, ํน์ ์ปฌ๋ผ์ null๋ก ์ ๋๋ก ๋งคํ์ด ์ด๋ฃจ์ด์ง์ง ์์์ต๋๋ค.
๋ถ์์ ํด๋ณด๋, ์ผ๋ฐ ๋ณ์๋ ๋งคํ์ด ์ ๋๊ณ ์์์ง๋ง, Collection ์์ ํน์ ์ปฌ๋ผ์๋ง null๋ก ๋งคํ๋๊ณ ์์์ต๋๋ค.
(๋ค๋ฅธ 1:N์ด ์๋ ์ผ๋ฐ์ ์ธ ์กฐํ(๋จ์ผ ๊ฐ์ฒด ๋งคํ)๋ ๊ธฐ๋ณธ์์ฑ์๋ก ๋งคํ์ ํ
์คํธํด๋ณธ ๊ฒฐ๊ณผ, ๋ ํน์ ์ปฌ๋ผ์ด null๋ก ๋งคํ์ด ์ ์์ ์ผ๋ก ์ด๋ฃจ์ด์ง์ง ์์์ต๋๋ค.)
@Builder
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class ProductLineWithProductsResponse {
private Long productLineId;
private Long memberId;
private Long categoryId;
private String name;
private String content;
private int price;
private Long totalStock;
private ProductLineStatus status;
private Long saleCount;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
private LocalDateTime deletedAt;
private String brandName;
private String biz_no;
private List<Product> productList;
}
<mapper namespace="org.store.clothstar.productLine.repository.ProductLineRepository">
<resultMap id="productWithOptions" type="org.store.clothstar.productLine.dto.response.ProductLineWithProductsResponse">
<id property="productLineId" column="product_line_id"/>
<result property="memberId" column="member_id"/>
<result property="categoryId" column="category_id"/>
<result property="name" column="name"/>
<result property="content" column="content"/>
<result property="price" column="price"/>
<result property="totalStock" column="total_stock"/>
<result property="status" column="status"/>
<result property="saleCount" column="sale_count"/>
<result property="createdAt" column="created_at"/>
<result property="modifiedAt" column="modified_at"/>
<result property="deletedAt" column="deleted_at"/>
<result property="brandName" column="brand_name"/>
<result property="biz_no" column="biz_no"/>
<collection property="productList" column="productLineId = product_line_id" javaType="List" ofType="org.store.clothstar.product.domain.Product" select="getProductsByProductLineId"/>
</resultMap>
<select id="selectProductLineWithOptions" resultMap="productWithOptions">
SELECT
pl.*,
s.brand_name,
s.biz_no
FROM product_line pl
INNER JOIN seller s ON pl.member_id = s.member_id
WHERE pl.product_line_id = #{productLineId}
</select>
<select id="getProductsByProductLineId" parameterType="org.store.clothstar.productLine.dto.response.ProductLineWithProductsResponse" resultType="org.store.clothstar.product.domain.Product">
SELECT *
FROM product
WHERE product_line_id = #{productLineId}
</select>
</mapper>
์์ ๊ฐ์ด ์์ฑํ ๊ฐ์ฒด์ mapper.xml ์ ํตํด ์กฐํ ๊ฒฐ๊ณผ๋ฅผ ๋งคํํ๋ฉด, ์๋์ ๊ฐ์ด 1:N์์ N์ธ Collection์๋ง ํน์ ์ปฌ๋ผ์ null์ด ๋ฐ์ํ์ต๋๋ค.
์์ธ์, ๊ธฐ๋ณธ์์ฑ์๋ฅผ ํตํด ๋งคํํ ๊ฒฝ์ฐ, ๋ณ์ ๋ค์ด๋ฐ๊ณผ column ์ด๋ฆ์ด ๊ฐ์ผ๋ฉด ์๋์ผ๋ก ๋งคํํด์ฃผ์ง๋ง, camelCase๊น์ง๋ ์ธ์์ด ์๋ฉ๋๋ค. (๋์๋ฌธ์๋ ๊ตฌ๋ณํ์ง ์์ต๋๋ค.)
์ฆ DB์ column๋ช
๊ณผ ์ ํํ ์ผ์นํด์ผ ์ธ์ํฉ๋๋ค.
๋ฐ๋ผ์ ์์ ๊ฐ์ด camelCase ์ค์ ์์ด, @NoArgsConstructor
์์ฑ์๊ฐ ์๋ ๊ฐ์ฒด์ resultMap, Collection์ ํตํด ๋งคํํ์ ๋ Collection์๋ง camelCase์ null๋ก ๋งคํ๋๋ ์ด์ ๋,
1. collection (Listํ์
๋ณ์) ์ ์ธ ์ผ๋ฐ ๋ณ์์๋ ์ง์ column์ ๋ช
์ํ๋ค.
2. collection ํ์
์์ ๋ณ์๋ค์๋ column์ ์ง์ ๋ช
์ํด์ฃผ์ง ์์๋ค ๋ฐ๋ผ์ camelCase๋ก ๋ ๋ณ์๋ค์ ๋งคํ์ด ์๋์ ์ผ๋ก ์๋๋ค .
๋๊ฐ์ง ์ด์ ๋๋ฌธ์ ๋๋ค.
๐งฉ collection์๋ ๊ฐ ํ๋ง๋ค resultํ (property + column)๋ฅผ ๋ช ์ํด์ค๋ค๋ฉด
camelCase ์ค์ ์์ด,@NoArgsConstructor
์์ฑ์๋ง ์์ด๋ ์ ์์ ์ผ๋ก ๋งคํ์ด ๊ฐ๋ฅํฉ๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ ๊ธฐ๋ณธ์์ฑ์๋ก ์ ์์ ์ผ๋ก ๋งคํํ ์ ์์๊น์?
๋ฐฉ๋ฒ์ ์ธ๊ฐ์ง๊ฐ ์กด์ฌํฉ๋๋ค.
์ธ๋์ค์ฝ์ด(_) -> ์นด๋ฉ์ผ์ด์ค
์๋ ๋ณํ ๊ธฐ๋ฅ ์ค์ ์๋ฐ์์ ํต์์ ์ผ๋ก ์ฌ์ฉํ๋ ๋ช ๋ช ๊ท์น์ ์นด๋ฉ์ผ์ด์ค ์ด๊ธฐ ๋๋ฌธ์, 2๋ฒ์ ์ ์ฉํ๊ธฐ๋ ์ด๋ ต๋ค๊ณ ํ๋จํ์ต๋๋ค.
๋ฐ๋ผ์ 1๋ฒ๊ณผ 3๋ฒ์ค์ ํํด์ผ ํ๋๋ฐ, ์ง์ select๋ฌธ์ alias๋ฅผ ๋ค ์ ๊ธฐ์๋ ์์ฐ์ฑ์ด ๋จ์ด์ง๋ค๊ณ ์๊ฐํ์ต๋๋ค.
๋ฐ๋ผ์ ์๋ฐ์ ํต์์ ์ธ ๋ช ๋ช ๊ท์น์ธ ์นด๋ฉ ์ผ์ด์ค๋ฅผ ์ฌ์ฉํ๋ฉด์๋, ์๋์ผ๋ก ๋งคํํ ์ ์๋๋ก 3๋ฒ ๋ฐฉ๋ฒ์ ์ ํํ์ฌ Mybatis์ camelCase๋ฅผ ์ค์ ํด์ฃผ๊ธฐ๋ก ํ์ต๋๋ค.
ํด๋น ๊ธฐ๋ฅ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ธ๋์ค์ฝ์ด(_)๋ก ๊ตฌ๋ถ๋ ์ปฌ๋ผ๋ช ์ ์๋ฐ์ ์นด๋ฉ ํ๊ธฐ๋ฒ(camelCase)์ ๋ง๊ฒ ์๋ ๋ณํํด์ฃผ๋ ๊ธฐ๋ฅ์ ๋๋ค.
3๋ฒ ๋ฐฉ๋ฒ์ ํํ์๋์ ์ฅ์ ์
1. ๋ฐ์ดํฐ๋ฒ ์ด์ค(์ธ๋๋ฐ_)์ ์๋ฐ ์ธ์ด(camelCase) ๊ฐ์ ์ฌ์ฉ๋๋ ๋ณ์๋ช
์ ์ผ๊ด์ฑ ์๊ฒ ์ ์งํ ์ ์๋ค.
2. ๊ฐ๋ฐ์๊ฐ ์ง์ ๋งคํ column์ ๋ช
์ํ ํ์๊ฐ ์์ด์ ธ์ ๊ฐ๋ฐ ์์ฐ์ฑ์ ํฅ์์ํฌ ์ ์๋ค
๋ ์ ์ ๋๋ค.
ํด๋น ๋ฐฉ๋ฒ์ ๋ค์ ํฌ์คํ ์์ ๋ฐ๋ก ๋ค๋ฃจ๋๋ก ํ๊ฒ ์ต๋๋ค.
[MyBatis - ๐ ๏ธTroubleShooting] Mybatis์ camelCase ๋ณํ ์ ์ฉํ๊ธฐ
MyBatis์์ ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ์ฌ์ฉํ์ฌ ๋งคํํ๋ ๊ฒฝ์ฐ, ์ปฌ๋ผ์ ์์๋ฅผ ์ผ์น์ํฌ ํ์๋ ์๋ค. ๋ณ์๋ช ๊ณผ ์ปฌ๋ผ๋ช ์ด ์์ ํ ์ผ์นํ๋ฉด ์๋์ผ๋ก ๋งคํ์ ์ํํ๋ค.
์์ฑ์ ๋งคํ์์ ํ๋ผ๋ฏธํฐ๋ camelCase๊น์ง ์๋์ผ๋ก ์ธ์ํ์ง ๋ชปํ๋ค.
๋ฐ๋ผ์ ๋ณ์๋ช ๊ณผ ์ปฌ๋ผ๋ช ์ด ์์ ํ ์ผ์นํ์ง ์๋ ๊ฒฝ์ฐ, ๋ค์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ํ๋๋ฅผ ๋ฐ๋ผ์ผ ํ๋ค.
๋ค์ ํฌ์คํ ์์๋
์ ๋ํด ๊ฐ๊ฐ ๋ค๋ฅธ ํฌ์คํ ์์ ๋ค๋ค๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.