[MyBatis - ๐Ÿ› ๏ธTroubleShooting] ๊ธฐ๋ณธ ์ƒ์„ฑ์ž์™€ ๊ฐ์ฒด ๋งคํ•‘์‹œ ์ฃผ์˜ํ•  ์ (ํŠน์ • ์ปฌ๋Ÿผ์ด null๋กœ ๋งคํ•‘๋˜์ง€ ์•Š๋Š” ํ˜„์ƒ)

Oguยท2024๋…„ 4์›” 9์ผ
1
post-thumbnail

๐Ÿ› ๏ธ์ƒํ™ฉ

ํ˜„์žฌ ์ง„ํ–‰์ค‘์ธ Mybatis ํ”„๋กœ์ ํŠธ์—์„œ Mybatis ๋งคํ•‘ ๊ฐ์ฒด (resultType ๋˜๋Š” resultMap)์— @NoArgsConstructor ์„ ๋ถ™์—ฌ ๊ฐ€์ ธ์˜ค๋ ค ํ•˜๋Š”๋ฐ ์ž๊พธ ํŠน์ • ๋ณ€์ˆ˜์—์„œ null์œผ๋กœ ๋งคํ•‘๋˜๊ณ  ์žˆ์ง€ ๋ชปํ•œ ์ƒํƒœ์˜€์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ‰์†Œ์—๋Š” @NoArgsConstructor ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  @Getter ์™€ @Builder ๋งŒ ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘ํ–ˆ๊ณ , ์ •์ƒ์ ์œผ๋กœ ์ „๋ถ€ ๋‹ค ์ž˜ ๋งคํ•‘์ด ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ Mybatis๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ๋งคํ•‘ํ•œ๋‹ค๊ณ  ๋ชจ๋“  ๊ธฐ์ˆ  ๋ธ”๋กœ๊ทธ์—์„œ ๋งํ•˜๊ณ  ์žˆ์–ด์„œ ์˜๋ฌธ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

  1. ์™œ ๋‚˜๋Š” ์ž๊พธ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ์ถ”๊ฐ€ํ•˜๋ฉด ํŠน์ • Column์ด null์ด์ง€?
  2. ์™œ ๋‚˜๋Š” @Getter ์™€ @Builder ๋งŒ ์‚ฌ์šฉํ•ด๋„ ์ •์ƒ์ ์œผ๋กœ ๋งคํ•‘๋˜์ง€?

ํ‰์†Œ ์˜๋ฌธ์ ์ด ์žˆ์—ˆ์ง€๋งŒ ๊ทธ๋ƒฅ ๊ธฐ๋Šฅ ๊ฐœ๋ฐœ์— ๋ฐ”๋น  ์˜๋ฌธ์ ์„ ๋ฌตํ˜€๋‘๊ณ  ์žˆ๋‹ค๊ฐ€, ๋˜ ๊ด€๋ จํ•˜์—ฌ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ์ด๋ฒˆ์—๋Š” ํ™•์‹คํžˆ ๋ถ„์„ํ•˜๊ณ  ํ•ด๊ฒฐํ•ด ๋ณด๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ ์‹œ์ ์€ Mybatis์—์„œ 1:N ๊ด€๊ณ„๋ฅผ List๋กœ ํฌํ•จํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๋ ค ํ•  ๋•Œ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
ORM์ธ JPA๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ๋Š” ์†์‰ฝ๊ฒŒ @OneToMany์™€ @ManyToOne ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Mybatis๋Š” ์ด๋Ÿฌํ•œ 1:N๊ด€๊ณ„๋ฅผ join sql์œผ๋กœ ๊ฐ€์ ธ์˜ค๊ธฐ ์œ„ํ•ด์„œ๋Š” resultMap๊ณผ collection์„ ์‚ฌ์šฉํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.
์ฆ‰, ๊ฐ์ฒด ์•ˆ์— ๊ฐ์ฒด๋ฅผ ๋งคํ•‘ํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š”

1. ๊ธฐ๋ณธ์ƒ์„ฑ์ž (@NoArgsArgument)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘ํ•  ๊ฒฝ์šฐ ์™œ ํŠน์ • Column์€ null๋กœ ๋งคํ•‘๋˜๋Š”๊ฐ€
2. ์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๋งคํ•‘ํ•  ๋•Œ ์ •์ƒ์ ์œผ๋กœ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€

์— ๋Œ€ํ•ด ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ—‚๏ธ 1. ๊ธฐ๋ณธ์ƒ์„ฑ์ž @NoArgsArgument๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘ํ•  ๊ฒฝ์šฐ, ์™œ ํŠน์ • Column์€ null๋กœ ๋งคํ•‘์ด ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์„๊นŒ?

๋‹ค์Œ๊ณผ ๊ฐ™์ด, ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋ฅผ ๊ฐ–๊ณ ์žˆ๋Š” ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ๋งคํ•‘์„ ํ•˜๋ ค ํ–ˆ์ง€๋งŒ, ํŠน์ • ์ปฌ๋Ÿผ์€ null๋กœ ์ œ๋Œ€๋กœ ๋งคํ•‘์ด ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
๋ถ„์„์„ ํ•ด๋ณด๋‹ˆ, ์ผ๋ฐ˜ ๋ณ€์ˆ˜๋Š” ๋งคํ•‘์ด ์ž˜ ๋˜๊ณ ์žˆ์—ˆ์ง€๋งŒ, Collection ์•ˆ์˜ ํŠน์ • ์ปฌ๋Ÿผ์—๋งŒ null๋กœ ๋งคํ•‘๋˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
(๋‹ค๋ฅธ 1:N์ด ์•„๋‹Œ ์ผ๋ฐ˜์ ์ธ ์กฐํšŒ(๋‹จ์ผ ๊ฐ์ฒด ๋งคํ•‘)๋„ ๊ธฐ๋ณธ์ƒ์„ฑ์ž๋กœ ๋งคํ•‘์„ ํ…Œ์ŠคํŠธํ•ด๋ณธ ๊ฒฐ๊ณผ, ๋˜ ํŠน์ • ์ปฌ๋Ÿผ์ด null๋กœ ๋งคํ•‘์ด ์ •์ƒ์ ์œผ๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.)

๋งคํ•‘ ๊ฐ์ฒด ProductLineWithProductsResponse

@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;
}

Mybatis Mapper.xml ์ฝ”๋“œ

<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. mapper.xml์— ์ง์ ‘ select๋ฌธ์— alias๋ฅผ ์ ์–ด์ค€๋‹ค.
  2. ๋งคํ•‘ ๊ฐ์ฒด์˜ ๋ณ€์ˆ˜๋ช…๊ณผ column ๋ช…์„ camelCase๊นŒ์ง€ ์™„์ „ํžˆ ์ผ์น˜์‹œํ‚จ๋‹ค.
  3. Mybatis ์„ค์ •์— ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์ปฌ๋Ÿผ๋ช…์–ธ๋”์Šค์ฝ”์–ด(_) -> ์นด๋ฉœ์ผ€์ด์Šค ์ž๋™ ๋ณ€ํ™˜ ๊ธฐ๋Šฅ ์„ค์ •

์ž๋ฐ”์—์„œ ํ†ต์ƒ์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๋ช…๋ช… ๊ทœ์น™์€ ์นด๋ฉœ์ผ€์ด์Šค ์ด๊ธฐ ๋•Œ๋ฌธ์—, 2๋ฒˆ์„ ์ ์šฉํ•˜๊ธฐ๋Š” ์–ด๋ ต๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ 1๋ฒˆ๊ณผ 3๋ฒˆ์ค‘์— ํƒํ•ด์•ผ ํ–ˆ๋Š”๋ฐ, ์ง์ ‘ select๋ฌธ์— alias๋ฅผ ๋‹ค ์ ๊ธฐ์—๋Š” ์ƒ์‚ฐ์„ฑ์ด ๋–จ์–ด์ง„๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์ž๋ฐ”์˜ ํ†ต์ƒ์ ์ธ ๋ช…๋ช… ๊ทœ์น™์ธ ์นด๋ฉœ ์ผ€์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด์„œ๋„, ์ž๋™์œผ๋กœ ๋งคํ•‘ํ•  ์ˆ˜ ์žˆ๋„๋ก 3๋ฒˆ ๋ฐฉ๋ฒ•์„ ์„ ํƒํ•˜์—ฌ Mybatis์— camelCase๋ฅผ ์„ค์ •ํ•ด์ฃผ๊ธฐ๋กœ ํ–ˆ์Šต๋‹ˆ๋‹ค.

๐Ÿฆ• Mybatis์— camelCase ์ž๋™ ๋ณ€ํ™˜ ์„ค์ •

ํ•ด๋‹น ๊ธฐ๋Šฅ์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ์–ธ๋”์Šค์ฝ”์–ด(_)๋กœ ๊ตฌ๋ถ„๋œ ์ปฌ๋Ÿผ๋ช…์„ ์ž๋ฐ”์˜ ์นด๋ฉœ ํ‘œ๊ธฐ๋ฒ•(camelCase)์— ๋งž๊ฒŒ ์ž๋™ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

3๋ฒˆ ๋ฐฉ๋ฒ•์„ ํƒํ–ˆ์„๋•Œ์˜ ์žฅ์ ์€

1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค(์–ธ๋”๋ฐ”_)์™€ ์ž๋ฐ” ์–ธ์–ด(camelCase) ๊ฐ„์— ์‚ฌ์šฉ๋˜๋Š” ๋ณ€์ˆ˜๋ช…์„ ์ผ๊ด€์„ฑ ์žˆ๊ฒŒ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.
2. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ๋งคํ•‘ column์„ ๋ช…์‹œํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ ธ์„œ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค

๋Š” ์ ์ž…๋‹ˆ๋‹ค.

ํ•ด๋‹น ๋ฐฉ๋ฒ•์€ ๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ ๋”ฐ๋กœ ๋‹ค๋ฃจ๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

[MyBatis - ๐Ÿ› ๏ธTroubleShooting] Mybatis์— camelCase ๋ณ€ํ™˜ ์ ์šฉํ•˜๊ธฐ

๐Ÿ—‚๏ธ ์ •๋ฆฌ

  1. MyBatis์—์„œ ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งคํ•‘ํ•˜๋Š” ๊ฒฝ์šฐ, ์ปฌ๋Ÿผ์˜ ์ˆœ์„œ๋ฅผ ์ผ์น˜์‹œํ‚ฌ ํ•„์š”๋Š” ์—†๋‹ค. ๋ณ€์ˆ˜๋ช…๊ณผ ์ปฌ๋Ÿผ๋ช…์ด ์™„์ „ํžˆ ์ผ์น˜ํ•˜๋ฉด ์ž๋™์œผ๋กœ ๋งคํ•‘์„ ์ˆ˜ํ–‰ํ•œ๋‹ค.

  2. ์ƒ์„ฑ์ž ๋งคํ•‘์—์„œ ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” camelCase๊นŒ์ง€ ์ž๋™์œผ๋กœ ์ธ์‹ํ•˜์ง€ ๋ชปํ•œ๋‹ค.

  3. ๋”ฐ๋ผ์„œ ๋ณ€์ˆ˜๋ช…๊ณผ ์ปฌ๋Ÿผ๋ช…์ด ์™„์ „ํžˆ ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ, ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋ฅผ ๋”ฐ๋ผ์•ผ ํ•œ๋‹ค.

  • mapper.xml์— ์ง์ ‘ select๋ฌธ์— alias๋ฅผ ์ ์–ด์ค€๋‹ค.
  • Mybatis ์„ค์ •์„ ํ†ตํ•ด camelCase๋„ ์ž๋™์œผ๋กœ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•œ๋‹ค.

๋‹ค์Œ ํฌ์ŠคํŒ…์—์„œ๋Š”

  1. [MyBatis - ๐Ÿ› ๏ธTroubleShooting] Mybatis์— camelCase ๋ณ€ํ™˜ ์ ์šฉํ•˜๊ธฐ
  1. @Getter ์™€ @Builder ๋งŒ ์‚ฌ์šฉํ•ด๋„ ์ •์ƒ์ ์œผ๋กœ ๋งคํ•‘๋๋˜ ์ด์œ 

์— ๋Œ€ํ•ด ๊ฐ๊ฐ ๋‹ค๋ฅธ ํฌ์ŠคํŒ…์—์„œ ๋‹ค๋ค„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ 

profile
็งใฏใ‚ฒใƒผใƒ ใจๆ—ฅๆœฌใŒๅฅฝใใชBackend Developerๅฟ—ๆœ›็”ŸใฎOguใงใ™๐Ÿค๐Ÿค

0๊ฐœ์˜ ๋Œ“๊ธ€