MyBatis Map이나 Bean 없이 2개 이상 파라미터 전달

MyBatis에서 Map이나 DTO Bean을 만들지 않고 여러 파라미터를 Mapper 메서드에 전달하는 방법입니다. 간단한 쿼리에서 불필요한 클래스 생성을 줄일 수 있습니다.

언제 사용하나요?

  • 간단한 조회에서 2-3개 파라미터 전달
  • DTO 클래스 생성이 과한 경우
  • 페이징 처리 (offset, limit)
  • 범위 검색 (startDate, endDate)

방법 1: @Param 어노테이션 (권장)

// Mapper 인터페이스
@Mapper
public interface UserMapper {
    
    // 단일 파라미터는 @Param 생략 가능
    User findById(Long id);
    
    // 2개 이상은 @Param 필수
    User findByEmailAndStatus(
        @Param("email") String email,
        @Param("status") String status
    );
    
    // 페이징
    List<User> findAllWithPaging(
        @Param("offset") int offset,
        @Param("limit") int limit
    );
    
    // 날짜 범위
    List<Order> findByDateRange(
        @Param("startDate") LocalDate startDate,
        @Param("endDate") LocalDate endDate,
        @Param("status") String status
    );
}

XML Mapper에서 사용

<!-- @Param으로 지정한 이름 그대로 사용 -->
<select id="findByEmailAndStatus" resultType="User">
    SELECT * FROM users
    WHERE email = #{email}
    AND status = #{status}
</select>

<!-- 페이징 -->
<select id="findAllWithPaging" resultType="User">
    SELECT * FROM users
    ORDER BY created_at DESC
    LIMIT #{limit} OFFSET #{offset}
</select>

<!-- 조건부 파라미터 -->
<select id="findByDateRange" resultType="Order">
    SELECT * FROM orders
    WHERE 1=1
    <if test="startDate != null">
        AND order_date >= #{startDate}
    </if>
    <if test="endDate != null">
        AND order_date <= #{endDate}
    </if>
    <if test="status != null and status != '">
        AND status = #{status}
    </if>
</select>

방법 2: param1, param2 사용

// @Param 없이 순서로 접근
List<User> findByAgeRange(int minAge, int maxAge);

<!-- XML에서 param1, param2로 접근 -->
<select id="findByAgeRange" resultType="User">
    SELECT * FROM users
    WHERE age BETWEEN #{param1} AND #{param2}
</select>

<!-- 또는 arg0, arg1 (비권장) -->
<select id="findByAgeRange" resultType="User">
    SELECT * FROM users
    WHERE age BETWEEN #{arg0} AND #{arg1}
</select>

<!-- param1, param2는 가독성이 낮아 @Param 권장 -->

방법 3: Map 사용

// Map으로 전달
List<User> findByConditions(Map<String, Object> params);

// 호출
Map<String, Object> params = new HashMap<>();
params.put("status", "ACTIVE");
params.put("minAge", 20);
params.put("maxAge", 30);
List<User> users = mapper.findByConditions(params);

<!-- XML -->
<select id="findByConditions" resultType="User">
    SELECT * FROM users
    WHERE status = #{status}
    AND age BETWEEN #{minAge} AND #{maxAge}
</select>

INSERT/UPDATE에서 사용

// 여러 파라미터로 UPDATE
int updateStatus(
    @Param("id") Long id,
    @Param("status") String status,
    @Param("updatedBy") String updatedBy
);

<update id="updateStatus">
    UPDATE users
    SET status = #{status},
        updated_by = #{updatedBy},
        updated_at = NOW()
    WHERE id = #{id}
</update>

// 조건부 INSERT
int insertIfNotExists(
    @Param("email") String email,
    @Param("name") String name
);

복잡한 조건 처리

<!-- 동적 IN 절 -->
List<User> findByIds(@Param("ids") List<Long> ids);

<select id="findByIds" resultType="User">
    SELECT * FROM users
    WHERE id IN
    <foreach collection="ids" item="id" 
             open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 여러 파라미터 + 리스트 -->
List<Order> findOrders(
    @Param("userId") Long userId,
    @Param("statuses") List<String> statuses
);

<select id="findOrders" resultType="Order">
    SELECT * FROM orders
    WHERE user_id = #{userId}
    AND status IN
    <foreach collection="statuses" item="status"
             open="(" separator="," close=")">
        #{status}
    </foreach>
</select>

주의사항

  • 파라미터가 3개 이상이면 DTO 클래스 권장
  • @Param 이름과 XML 변수명 정확히 일치해야 함
  • null 값 처리를 위한 조건문 추가 필요
  • MyBatis 3.x 이상에서 @Param 사용 가능