본문 바로가기
백엔드/스프링

[Spring] MyBatis XML 연동 개요

by AI읽어주는남자 2025. 10. 13.
반응형

Spring Boot & MyBatis XML 연동 요약

1. 개요

이 문서는 Spring Boot 환경에서 MyBatis 프레임워크를 사용하여 SQL 쿼리를 Java 코드로부터 분리된 XML 파일로 관리하는 방법에 대해 설명합니다. 이 방식은 SQL의 유지보수성을 높이고, Java 코드의 가독성을 향상시킵니다.


2. 주요 파일 및 역할

📄 XmlMapper.xml - SQL 쿼리 정의

MyBatis의 핵심으로, 실제 실행될 SQL 쿼리를 담고 있는 XML 파일입니다.

  • <mapper namespace="...">: 이 XML 파일과 연결될 Java 인터페이스(Mapper)의 전체 경로를 지정합니다.
  • <insert>, <select>, <update>, <delete>: SQL 문 종류에 맞는 태그를 사용합니다.
  • id 속성: Mapper 인터페이스의 메소드 이름과 일치시켜 서로 매핑합니다.
  • parameterType 속성: SQL문에 전달될 파라미터의 데이터 타입을 지정합니다.
  • #{필드명}: 파라미터로 전달된 객체(DTO)의 필드 값을 SQL문에 삽입하는 문법입니다.
<?xml version="1.0" encoding="UTF-8" ?>
<!-- XML 이란? 마크업 이용한 데이터 저장(설정)/전달(매핑)/교환(API) 마크업언어 -->
<!-- HTML : 마크업 이용한 웹페이지 마크업언어 -->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org/DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- XML 파일내 mybatis 라이브러리 설정 -->

<!-- xml과 매핑할 인터페이스 설정 , [src] 이하 경로 지정  -->
<!-- <mapper namespace="매핑할인터페이스파일경로"> -->
<mapper namespace="example.day13.XmlMapper">    <!-- mapper 시작 -->
    <!-- [1] 등록 -->
    <!-- <insert id="추상메소드명" parameterType="매개변수타입(클래스) 파일경로" > -->
    <insert id="save" parameterType="example.day13.StudentDto" useGeneratedKeys="true" keyProperty="sno">
        INSERT INTO student(name, kor, math)
        values( #{name} , #{kor} , #{math} );
    </insert>

    <!-- [2] 전체조회 -->
    <select id="findAll" resultType="example.day13.StudentDto">
        select * from student;
    </select>

    <!-- [3] 개별조회 -->
    <select id="find" parameterType="int" resultType="example.day13.StudentDto">
        select * from student where sno = #{sno};
    </select>

    <!-- [4] 개별삭제 : 반환타입 설정하지 않고 int일 때는 삭제/수정/삽입 레코드 수 반환 -->
    <delete id="delete" parameterType="int">
        delete from student where sno = #{sno};
    </delete>

    <!-- [5] 개별수정 -->
    <update id="update" parameterType="example.day13.StudentDto">
        update student set kor = #{kor} , math = #{math} where sno = #{sno};
    </update>


</mapper> <!-- mapper 끝 -->


XmlMapper.java - Mapper 인터페이스

SQL 쿼리를 호출하는 메소드를 정의하는 Java 인터페이스입니다.

  • @Mapper: Spring Boot가 이 인터페이스를 MyBatis Mapper로 인식하고 Bean으로 등록하도록 합니다.
  • 추상 메소드: XML 파일의 id와 동일한 이름의 메소드를 선언합니다. 실제 구현은 MyBatis가 XML을 기반으로 자동으로 생성합니다.
package example.day13;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;

import java.util.List;

@Mapper
public interface XmlMapper {

    // [1] 등록
    // MyBatis에서 SQL 매핑하는 방법
    // 방법1 : 추상메소드 위에 @Insert("SQL")
    // 방법2 : 추상메소드를 매핑하는 XML파일에서 SQL 작성
    // @Insert("insert into student(name, kor, math) values(#{name}, #{kor}, #{math}; ")
    // @Options(useGeneratedKeys = true , keyProperty = "sno") // autoincrement key(PK값) 반환
    // 어노테이션 vs Xml 하나만 해라
    int save (StudentDto dto);

    // [2] 전체조회
    List<StudentDto> findAll();

    // [3] 개별조회
    StudentDto find(int sno);

    // [4] 개별삭제
    int delete(int sno);

    // [5] 개별수정
    int update(StudentDto dto);

} // inter end

🎮 XmlController.java - REST 컨트롤러

HTTP 요청을 받아 비즈니스 로직을 처리하고 응답하는 계층입니다.

  • @RestController: 이 클래스가 RESTful API의 엔드포인트임을 나타냅니다.
  • 의존성 주입 (DI): @RequiredArgsConstructorprivate final XmlMapper xmlMapper;를 통해 XmlMapper 인터페이스의 구현체를 주입받습니다.
  • 메소드 호출: 주입받은 xmlMapper 객체의 메소드(save, findAll)를 호출하여 데이터베이스 작업을 수행합니다.
package example.day13;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/xml")
@RequiredArgsConstructor
public class XmlController {
    // DI
    private final XmlMapper xmlMapper;

    // [1] 등록
    // http://localhost:8080/xml/
    // { "name" : "유재슥" , "kor" : 97 , "math" : 86 }
    @PostMapping("")
    public ResponseEntity<?> save (@RequestBody StudentDto dto){
        // < ? > : 제네릭타입에 ? 넣으면 모든 타입을 지칭한다. 와일드카드
        xmlMapper.save(dto);
        return ResponseEntity.ok(true); // 제네릭 ? 이므로 모든 자료 대입
    }

    // [2] 전체조회
    // http://localhost:8080/xml/
    @GetMapping("")
    public ResponseEntity<?> findAll(){
        List<StudentDto> result = xmlMapper.findAll();
        return ResponseEntity.ok( result );
    }

    // [3] 개별조회
    // http://localhost:8080/xml/find?sno=1
    @GetMapping("/find")
    public ResponseEntity<?> find(@RequestParam int sno){
        StudentDto result = xmlMapper.find(sno);
        return ResponseEntity.ok( result );
    }

    // [4] 개별삭제
    // http://localhost:8080/xml?sno=1
    @DeleteMapping("")
    public ResponseEntity<?> delete(@RequestParam int sno){
        int result = xmlMapper.delete(sno);
        return ResponseEntity.ok(result);
    }

    // [5] 개별수정
    // http://localhost:8080/xml/
    // { "sno" : 1 , "kor" : 97 , "math" : 86 }
    @PutMapping("")
    public ResponseEntity<?> update(@RequestBody StudentDto dto){
        xmlMapper.update(dto);
        return ResponseEntity.ok(true);
    }

    
} // class end

📦 StudentDto.java - 데이터 전송 객체

계층 간 데이터 교환을 위해 사용되는 객체입니다. 데이터베이스 테이블의 구조와 유사하게 정의됩니다.

  • @Data, @Builder, 등: Lombok 어노테이션을 사용하여 Getter, Setter, 생성자 등의 코드를 자동으로 생성합니다.
package example.day13;

import lombok.*;

@Data @Builder
@NoArgsConstructor @AllArgsConstructor
public class StudentDto {
    private int sno;
    private String name;
    private int kor;
    private int math;
}

🚀 AppStart.java - 애플리케이션 시작점

Spring Boot 애플리케이션을 실행하는 메인 클래스입니다.

  • @SpringBootApplication: Spring Boot의 핵심 설정을 자동으로 구성합니다.
package example.day13;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AppStart {
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class);
    }
}

3. 전체 동작 흐름

  1. HTTP 요청: 클라이언트가 REST API(예: POST /xml)를 호출합니다.
  2. @RestController (XmlController): 요청을 받아 해당 엔드포인트와 매핑된 save 메소드를 실행합니다.
  3. @Mapper (XmlMapper): 컨트롤러는 주입받은 XmlMappersave 메소드를 호출합니다.
  4. MyBatis: XmlMapper 인터페이스와 연결된 XmlMapper.xml 파일에서 id="save"<insert> 태그를 찾아 정의된 SQL을 실행합니다.
  5. 데이터베이스: SQL이 실행되어 데이터가 등록됩니다.
  6. 응답: 실행 결과가 역순으로 컨트롤러까지 전달되고, 클라이언트에게 최종 응답(ResponseEntity)을 보냅니다.

참조1. application.properties 설정

# [4] 서버 실행할 때 SQL 파일 실행 설정 : 개발운영
# 순서 : DB 실행 -> AppStart -> DB 사용
# Spring 서버를 실행할 때마다 초기 sql 세팅할 수 있다. (샘플데이터 클리어)
# always(사용함) , never(사용안함)
spring.sql.init.mode=always
# spring.sql.init.mode=never

# schema.sql : DDL(drop, create 등) 파일 , data.sql : DML(insert) 파일
# 주의사항1 : 데이터베이스 생성은 불가능하다. 워크벤치에서 직접 생성/삭제하기
#        DROP DATABASE IF EXISTS springweb2;
#        CREATE DATABASE springweb2;
#        USE springweb2;
#        위에 것들 실행한 후 AppStart -> 이후 DB 사용(CRUD) 가능
# 주의사항2 : DROP TABLE IF EXISTS 테이블명; 테이블 DROP SQL 필요함. 관계테이블일 때는 FK(참조테이블) 먼저 삭제.
# classpath : 프로젝트내 resource 폴더 지칭
#       classpath:/폴더명/파일명.sql
spring.sql.init.schema-locations=classpath:/sql/schema.sql
spring.sql.init.data-locations=classpath:/sql/data.sql

# [5] MyBatis 설정 , MyBatis에서 SQL 사용하는 방법 : (1) @어노테이션 (2) XML
# Mapper XML 파일 경로 지정
# mybatis.mapper-locations=classpath:/폴더명/파일명.xml , *(와일드카드) : 모든파일 지정
mybatis.mapper-locations=classpath:/mappers/*.xml

 

참조2. SQL문 예시

1. Schema

-- --------------------------------------- 실습1 ----------------------------------------
DROP TABLE IF EXISTS products;
CREATE TABLE products (
    product_id INT PRIMARY KEY AUTO_INCREMENT, -- 상품 ID (자동 증가)
    product_name VARCHAR(255) NOT NULL,        -- 상품명
    stock_quantity INT NOT NULL                -- 재고 수량
);

-- --------------------------------------- day06 example ----------------------------------------
-- 학생 테이블
DROP TABLE IF EXISTS student;
CREATE TABLE student (
    sno INT AUTO_INCREMENT,              -- 학생 번호 (자동 증가)
    name VARCHAR(50) NOT NULL,           -- 이름
    kor INT NOT NULL,                    -- 국어 점수
    math INT NOT NULL,                    -- 수학 점수
    CONSTRAINT  PRIMARY KEY (sno)  -- 기본키 제약 조건 추가
);

-- --------------------------------------- day07 boardService13 ----------------------------------------
DROP TABLE IF EXISTS board;
create table board(
    bno int auto_increment ,
    bcontent longtext not null ,
    bwriter varchar(30) not null ,
    constraint primary key(bno)
);

-- --------------------------------------- day09 trans ----------------------------------------
DROP TABLE IF EXISTS trans;
CREATE TABLE trans(
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    money INT UNSIGNED DEFAULT 0
);

-- --------------------------------------- 실습3  ----------------------------------------
-- fk를 먼저 삭제해야함

DROP TABLE IF EXISTS rentals;
DROP TABLE IF EXISTS books;
-- 1. 책 테이블

CREATE TABLE books (
    id INT NOT NULL AUTO_INCREMENT ,
    title VARCHAR(255) NOT NULL,
    stock INT NOT NULL DEFAULT 0,
    PRIMARY KEY (id)
);

-- 2. 대출 기록 테이블

CREATE TABLE rentals (
    id INT NOT NULL AUTO_INCREMENT,
    book_id INT NOT NULL,
    member VARCHAR(100) NOT NULL,
    rent_date DATETIME DEFAULT NOW(),
    return_date DATETIME NULL,
    PRIMARY KEY (id),
    FOREIGN KEY (book_id) REFERENCES books(id)
);

 

2. Data

INSERT INTO products (product_name, stock_quantity) VALUES
('무선 이어폰', 25),
('스마트워치', 12),
('게이밍 키보드', 30),
('기계식 마우스', 8),
('휴대용 충전기', 15);


-- --------------------------------------- day06 example ----------------------------------------

INSERT INTO student (name, kor, math) VALUES ('홍길동', 85, 90);
INSERT INTO student (name, kor, math) VALUES ('김철수', 78, 88);
INSERT INTO student (name, kor, math) VALUES ('이영희', 92, 95);
INSERT INTO student (name, kor, math) VALUES ('박지민', 70, 65);
INSERT INTO student (name, kor, math) VALUES ('최유리', 88, 82);

-- --------------------------------------- day07 boardService13 ----------------------------------------

INSERT INTO board (bcontent, bwriter) VALUES ('안녕하세요', '유재석');
INSERT INTO board (bcontent, bwriter) VALUES ('오늘도 좋은 하루 되세요!', '김태호');
INSERT INTO board (bcontent, bwriter) VALUES ('점심 뭐 드셨나요?', '박명수');
INSERT INTO board (bcontent, bwriter) VALUES ('날씨가 참 좋네요', '정준하');
INSERT INTO board (bcontent, bwriter) VALUES ('이번 주말 계획은?', '하하');
INSERT INTO board (bcontent, bwriter) VALUES ('파이팅입니다!', '이광수');
INSERT INTO board (bcontent, bwriter) VALUES ('다들 행복하세요', '송지효');
INSERT INTO board (bcontent, bwriter) VALUES ('무한도전 그립다', '노홍철');
INSERT INTO board (bcontent, bwriter) VALUES ('커피 한 잔의 여유', '길');
INSERT INTO board (bcontent, bwriter) VALUES ('오늘은 집에서 푹 쉬어요', '정형돈');
INSERT INTO board (bcontent, bwriter) VALUES ('운동은 하셨나요?', '김종국');
INSERT INTO board (bcontent, bwriter) VALUES ('영화 추천해 주세요', '양세찬');
INSERT INTO board (bcontent, bwriter) VALUES ('책 읽기 좋은 날이네요', '전소민');
INSERT INTO board (bcontent, bwriter) VALUES ('비가 와서 기분이 우울해요', '유희열');
INSERT INTO board (bcontent, bwriter) VALUES ('점심 메뉴 고민중...', '이효리');
INSERT INTO board (bcontent, bwriter) VALUES ('졸려요...', '강호동');
INSERT INTO board (bcontent, bwriter) VALUES ('주말까지 며칠 남았죠?', '이수근');
INSERT INTO board (bcontent, bwriter) VALUES ('야근은 너무 싫어요', '서장훈');
INSERT INTO board (bcontent, bwriter) VALUES ('오늘 기온이 몇 도일까요?', '장도연');
INSERT INTO board (bcontent, bwriter) VALUES ('여름엔 역시 냉면', '홍진경');
INSERT INTO board (bcontent, bwriter) VALUES ('지금 듣는 노래는?', '장성규');
INSERT INTO board (bcontent, bwriter) VALUES ('퇴근하고 싶다', '이상민');
INSERT INTO board (bcontent, bwriter) VALUES ('다이어트는 내일부터', '박나래');
INSERT INTO board (bcontent, bwriter) VALUES ('버스 기다리는 중', '김숙');
INSERT INTO board (bcontent, bwriter) VALUES ('늦잠 잤어요', '이영자');
INSERT INTO board (bcontent, bwriter) VALUES ('스트레스 풀고 싶다', '유세윤');
INSERT INTO board (bcontent, bwriter) VALUES ('아이스 아메리카노 최고', '김신영');
INSERT INTO board (bcontent, bwriter) VALUES ('게임 하다 왔어요', '이홍기');
INSERT INTO board (bcontent, bwriter) VALUES ('일상 공유해요~', '김재중');
INSERT INTO board (bcontent, bwriter) VALUES ('오늘 하루 어땠나요?', '장나라');
INSERT INTO board (bcontent, bwriter) VALUES ('마라탕 먹고 싶다', '현아');
INSERT INTO board (bcontent, bwriter) VALUES ('퇴근길 너무 막혀요', '정은지');
INSERT INTO board (bcontent, bwriter) VALUES ('곧 여행 가요!', '수지');
INSERT INTO board (bcontent, bwriter) VALUES ('카페에서 힐링 중', '아이유');
INSERT INTO board (bcontent, bwriter) VALUES ('노래 추천해 주세요~', '로제');
INSERT INTO board (bcontent, bwriter) VALUES ('배가 너무 고파요', '지수');
INSERT INTO board (bcontent, bwriter) VALUES ('헬스장 다녀왔어요', '제니');
INSERT INTO board (bcontent, bwriter) VALUES ('오늘도 화이팅!', '리사');
INSERT INTO board (bcontent, bwriter) VALUES ('운전 조심하세요~', '태연');

-- --------------------------------------- day09 trans ----------------------------------------

INSERT INTO trans (name, money) VALUES
('신동엽', 200000),
('서장훈', 200000);

-- --------------------------------------- 실습3  ----------------------------------------

-- 3. 샘플 데이터 (책 목록)
INSERT INTO books (id, title, stock) VALUES (1, '자바의 정석', 3);
INSERT INTO books (id, title, stock) VALUES (2, '스프링 인 액션', 2);
INSERT INTO books (id, title, stock) VALUES (3, '토비의 스프링', 1);
INSERT INTO books (id, title, stock) VALUES (4, '리액트 교과서', 5);

-- 4. 샘플 데이터 (대출 기록)
INSERT INTO rentals (id, book_id, member) VALUES (1, 1, '홍길동');

 

3. Sample (해당 파일 workbench에서 실행 후 AppStart할 것)

DROP DATABASE IF EXISTS springweb2;
CREATE DATABASE springweb2;
USE springweb2;
반응형