글 작성자: beaniejoy

📌 MyBatis 사용함에 있어서 살짝의 아쉬움... (그래도 좋다!)

MyBatis를 사용하면서 SQL 구문과 객체 관점을 매핑해준다는 점에서 많은 장점이 있습니다. 지연 로딩 등의 여러 유용한 기능들도 제공해주기도 하고 JPA와 같이 사용하면서 성능 조절을 할 수 있다는 점도 장점이라고 할 수 있겠네요.

하지만 제가 사용하면서 느낀점은 개발할 때 Entity Class와 Mapper xml 설정 내용 둘다 유심히 잘 봐야한다는 점에서 번거로움이 있었습니다. 특히 xml 설정 내용은 철자 하나하나 정확하게 작성해야 하고 java 코드가 아니라 쌩 SQL 구문을 이용해야 한다는 점에서 살짝 불편함을 느꼈습니다.

그 중 제가 자주 에러를 마주했던 부분에 대해 기록하고자 합니다.

 

📌 기본적인 INSERT 쿼리에 대한 MyBatis 매핑

@Mapper
public interface UserRepository {
    void insert(@Param("user") User user);

    //...
}
<mapper namespace="kr.fiveminutesmarket.user.repository.UserRepository">
    <insert id="insert" parameterType="map" useGeneratedKeys="true" keyProperty="userId">
        INSERT INTO user(user_name, email, password, address, phone_number, seller, role_type_id, salt)
        VALUES(
               #{user.userName},
               #{user.email},
               #{user.password},
               #{user.address},
               #{user.phoneNumber},
               #{user.seller},
               #{user.roleType.roleTypeId},
               #{user.salt}
               )
    </insert>
</mapper>

parameterTypemap 방식으로 해서 작성한 것입니다. 여기서 Parameter로 사용하는 User 객체에 대해 #{...} 에 매핑하려는 property에 대한 getter가 있어야 합니다. 그러지 않으면 MyBatis를 통해 해당 쿼리를 요청할 때 getter로 접근을 못한다는 에러를 반환할 것입니다.

 

 

 

📌 Entity 클래스 property가 또 다른 클래스 객체라면

public class OrderProduct {

    private Long orderProductId;

    private String productName;

    private Amount amount;

    private Price price;

    private Long orderId;

    //...
}

DDD 개념을 한 번 도입해보면서 OrderProduct안에 amount, price를 각각 관리하는 영역을 따로 클래스화해서 구분지었습니다. 이에 대한 mapper xml insert 설정은 다음과 같습니다.

<mapper namespace="kr.fiveminutesmarket.order.repository.OrderProductRepository">
    <insert id="insert" parameterType="map" useGeneratedKeys="true" keyProperty="orderProductId">
        INSERT INTO order_product(product_name, amount, price, order_id)
        VALUES(#{orderProduct.productName}, #{orderProduct.amount}, #{orderProduct.price}, #{orderProduct.orderId})
    </insert>
</mapper>

이에 대한 order_product 테이블은 다음과 같습니다.

CREATE TABLE `order_product` (
    `order_product_id` int NOT NULL AUTO_INCREMENT,
    `product_name` varchar(40) NOT NULL,
    `amount` int NOT NULL,
    `price` int NOT NULL,
    `order_id` int NOT NULL,
    PRIMARY KEY (`order_product_id`),
    KEY `order_id` (`order_id`),
    FOREIGN KEY (`order_id`) REFERENCES `order` (`order_id`)
)

중요한 것은 DB상에서 amount, price 칼럼은 int type으로 설정되어 있지만 java class에서는 Amount, Price class로 선언되어 있습니다.

만약 위와 같은 상황에서 다음과 같이 getter를 작성한다면 에러가 발생할 것입니다.

// OrderProduct
public Amount getAmount() {
    return amount;
}

public Price getPrice() {
    return price;
}
//...

xml insert 쿼리 구문에서 #{orderProduct.amount} (Amount class)amount (int) column을 매핑하고 있습니다. 서로 맞지 않는 타입이기에 에러가 나오는 것은 당연한 일입니다.

 

 

차라리 다음과 같은 방식으로 하는 것이 좋습니다.

// OrderProduct
public Integer getAmount() {
    return amount.getAmount();
}

public Integer getPrice() {
    return price.getPrice();
}

// Amount
public Integer getAmount() {
    return amount;
}

// Price
public Intger getPrice() {
    return price;
}

 

📌 정리

이런 방식 외에도 여러 방법들이 존재하니 상황에 맞게 작성하면 될 것 같습니다. 중요한 것은 DB table 상의 type과 Entity class property의 type, getter의 존재유무와 getter의 return type 등 여러 요소들을 고려해 어긋나지 않게 잘 매핑하는 것이 MyBatis를 사용함에 있어 중요하다고 생각합니다.