▣ PART · DB

Mapper 인터페이스 ↔ XML

두 파일이 어떻게 짝지어지는가

학습 목표

  • Mapper 인터페이스 + XML 의 매칭 규칙 4 가지를 안다
  • resultType, parameterType, #{} 사용
  • useGeneratedKeys 로 INSERT 후 ID 받기
  • JOIN 결과 매핑

⚠️ 두 파일이 어떻게 연결?

학생이 자주 하는 질문

"인터페이스에는 메서드 시그니처만 있고, XML 에는 SQL 만 있는데 — 어떻게 연결되어서 동작하지?"

이름 매칭」 의 마법.

🛠️ 4 가지 매칭 규칙

namespace · id · 파일 위치 · 패키지 스캔

인터페이스와 XML 이 짝지어지는 데 필요한 4 가지 일치.

① namespace ↔ 인터페이스 풀 클래스명


// src/main/java/com/smhrd/mapper/BoardMapper.java
package com.smhrd.mapper;

@Mapper
public interface BoardMapper { ... }

<!-- src/main/resources/com/smhrd/mapper/BoardMapper.xml -->
<mapper namespace="com.smhrd.mapper.BoardMapper">
                          ↑
                   풀 클래스명과 완전히 일치

② id ↔ 메서드 이름


public interface BoardMapper {
    Board selectOne(int num);     // ← 메서드명
}

<mapper namespace="com.smhrd.mapper.BoardMapper">

    <select id="selectOne"
            parameterType="int"
            resultType="com.smhrd.domain.Board">
            ↑
       메서드 이름과 정확히 일치
        SELECT num, title, writer, content
        FROM myboard WHERE num = #{num}
    </select>

</mapper>

③ XML 파일 위치 — root-context 의 mapperLocations


<!-- root-context.xml -->
<bean id="sqlSessionFactory" ...>
    <property name="mapperLocations"
              value="classpath:com/smhrd/mapper/*Mapper.xml" />
</bean>

👉 인터페이스와 같은 경로(com/smhrd/mapper/) 의 *Mapper.xml 자동 등록.

④ 인터페이스 위치 — MapperScannerConfigurer


<!-- root-context.xml -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.smhrd.mapper" />
</bean>

👉 이 패키지 안의 모든 @Mapper 인터페이스를 Bean 으로 자동 등록.

완전한 매칭 그림

Service: mapper.selectOne(3); ↓ BoardMapper.java Board selectOne(int num); ↓ namespace + id 매칭 BoardMapper.xml <mapper namespace="com.smhrd.mapper.BoardMapper"> <select id="selectOne"> SELECT num, title, writer, content FROM myboard WHERE num=#{num} </select> </mapper>

resultType — 결과 매핑


<!-- 단일 객체 -->
<select id="selectOne"
        parameterType="int"
        resultType="com.smhrd.domain.Board">
    SELECT num, title, writer, content
    FROM myboard WHERE num = #{num}
</select>

<!-- List 반환 -->
<select id="selectList" resultType="com.smhrd.domain.Board">
    SELECT num, title, writer, content
    FROM myboard ORDER BY num DESC
</select>
<!-- 인터페이스에서 List<Board> 라고 선언했으면 자동 List -->

<!-- 단순 타입 -->
<select id="count" resultType="int">
    SELECT COUNT(*) FROM myboard
</select>

#{} — 파라미터 바인딩


// 인터페이스
Board selectOne(int num);
Board selectByWriter(@Param("writer") String writer);

<!-- 단일 파라미터 — 이름 자유 -->
<select ...> WHERE num = #{num} </select>

<!-- 객체 — getter 호출 -->
<insert id="insert" parameterType="com.smhrd.domain.Board">
    INSERT INTO myboard(title, writer, content)
    VALUES(#{title}, #{writer}, #{content})
</insert>

useGeneratedKeys — INSERT 후 num 받기


<insert id="insert"
        parameterType="com.smhrd.domain.Board"
        useGeneratedKeys="true" keyProperty="num">
    INSERT INTO myboard(title, writer, content)
    VALUES(#{title}, #{writer}, #{content})
</insert>

Board b = new Board();
b.setTitle("새 글");
b.setWriter("hong");
b.setContent("안녕하세요");
mapper.insert(b);

System.out.println(b.getNum());   // ⭐ AUTO_INCREMENT 결과 자동 채워짐 (예: 7)

컬럼 ↔ 필드 자동 매핑


<!-- DB 컬럼명: snake_case (created_at) -->
<!-- Java 필드: camelCase (createdAt)   -->
<select id="selectList" resultType="com.smhrd.domain.Board">
    SELECT num, title, writer, content
    FROM myboard ORDER BY num DESC
</select>

<!-- mybatis-config.xml 에서 한 줄 설정 -->
<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

👉 컬럼명이 같은 칸(num, title, writer, content)이면 그대로 매핑. 이후 차시에서 created_atcreatedAt 같은 변환이 필요해지면 이 옵션이 핵심.

resultMap — 명시적 매핑


<resultMap id="boardMap" type="com.smhrd.domain.Board">
    <id     property="num"     column="num" />
    <result property="title"   column="title" />
    <result property="writer"  column="writer" />
    <result property="content" column="content" />
</resultMap>

<select id="selectList" resultMap="boardMap">
    SELECT num, title, writer, content FROM myboard
</select>

👉 복잡한 매핑이 필요할 때 사용. 본 과정은 mapUnderscoreToCamelCase 만으로 대부분 충분.

흔한 함정

증상원인
"Invalid bound statement"namespace ↔ 풀 클래스명 / id ↔ 메서드명 불일치
"No qualifying bean of type 'XMapper'"MapperScannerConfigurer basePackage 잘못
XML 못 찾음mapperLocations 경로 잘못
필드 모두 nullmapUnderscoreToCamelCase 누락
INSERT 후 id 가 0useGeneratedKeys 누락

🔄 Before / After

전 차시 끝

Mapper 가 동작은 하지만 어떻게 연결되는지 막연.

이번 차시 끝

4 가지 매칭 규칙을 안다. resultType·#{}·useGeneratedKeys 사용.

정리

오늘 들고 가는 것

  • 매칭 4 규칙: namespace · id · XML 위치 · 인터페이스 위치
  • resultType / List 자동 매핑 / 단순 타입
  • #{} 으로 파라미터 안전 바인딩
  • useGeneratedKeys 로 INSERT 후 ID 자동 채움