MyBatis의 사용법 (Spring Boot, Gradle, MySQL)

leedong617·2024년 9월 18일
post-thumbnail

MyBatis는 자바 기반의 SQL 매퍼 프레임워크로, SQL 쿼리와 자바 객체 간의 매핑을 지원하여 개발자가 SQL을 직접 작성하면서도 객체 지향 프로그래밍의 이점을 누릴 수 있도록 해줌. MyBatis는 복잡한 JDBC 코드를 단순화하고, 간단한 설정으로 데이터베이스와의 상호 작용을 효율적으로 관리할 수 있게 해줌.

MyBatis의 기본 개념


SQL 매퍼 프레임워크

MyBatis는 SQL 문과 자바 객체를 매핑하여, SQL 결과를 자바 객체로 변환하거나 자바 객체를 이용하여 SQL을 실행할 수 있음.

XML 및 어노테이션 기반 설정

SQL 쿼리와 매핑 정보를 XML 파일이나 어노테이션을 통해 설정함.

동적 SQL 지원

조건에 따라 SQL 문을 동적으로 생성할 수 있는 기능을 제공함.

MyBatis의 주요 구성 요소


SqlSessionFactory

SqlSessionFactory는 MyBatis에서 SqlSession 객체를 생성하는 팩토리로, 데이터베이스와의 세션을 관리하고 SQL 문을 실행하는 핵심 역할을 함. Spring Boot와 MyBatis는 SqlSessionFactory를 자동으로 설정하고 관리하기 때문에, 개발자는 이를 직접 설정하지 않아도 됨.

SqlSession

SqlSession은 MyBatis에서 SQL 쿼리를 실행하거나, Mapper 인터페이스와 상호작용하여 데이터베이스 작업을 처리하는 역할을 함. SqlSession은 selectOne(), selectList(), insert(), update(), delete() 등의 메서드를 통해 SQL 문을 실행할 수 있음. Spring Boot와의 통합에서는 대부분 Mapper 인터페이스를 통해 SqlSession을 사용하게 됨.

Mapper 인터페이스

Mapper 인터페이스는 자바 메소드와 SQL 쿼리를 매핑하는 역할을 함. Mapper는 Spring Boot와 MyBatis의 자동 설정을 통해 빈으로 등록되며, SQL 문과 자바 메소드를 직접 연결하여 데이터를 처리함.

@Mapper
public interface UserMapper {
    User selectUserById(int id);
    void insertUser(User user);
}

@Mapper 어노테이션을 사용하여 Spring Boot가 이 인터페이스를 MyBatis Mapper로 인식하게 함.
각 메서드는 Mapper XML 파일 또는 어노테이션을 사용하여 SQL 쿼리와 직접 매핑됨.

MyBatis의 주요 특징


직접적인 SQL 제어

개발자가 SQL 문을 직접 작성하므로, 복잡한 쿼리나 고성능 쿼리를 구현하기 쉬움.
SQL의 최적화를 직접 관리할 수 있어 성능 튜닝에 유리함.

간단한 설정과 사용법

설정이 비교적 간단하며, 학습 곡선이 완만하여 빠르게 적용할 수 있음.
JDBC 코드의 반복적인 부분을 줄여주어 개발 생산성을 높임.

동적 SQL 지원

<if> <choose> <foreach> 등의 태그를 사용하여 동적 SQL을 쉽게 작성할 수 있음.
조건에 따라 SQL 문을 유연하게 생성하여 다양한 상황에 대응할 수 있음.

객체 매핑

SQL 결과를 자바 객체로 매핑하여 코드에서 편리하게 사용할 수 있음.
ResultMap을 통해 복잡한 객체 매핑도 처리할 수 있음.

Spring Boot 환경에서의 MyBatis의 사용법 및 예시 (Gradle, MySQL, Lombok, thymeleaf)


build.gradle 파일에 MyBatis, MySQL, Lombok 의존성 추가

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.mysql:mysql-connector-j'
}

application.properties 설정

MyBatis가 데이터베이스와 상호작용할 수 있도록 application.properties 파일에 데이터베이스 연결 정보를 설정 및 MyBatis 설정.
Spring Boot는 이 설정을 통해 데이터베이스와 연결을 설정하고, SqlSessionFactory를 생성함.

# MySQL 데이터베이스 연결 정보
spring.datasource.url=jdbc:mysql://localhost:3306(연결할 port)/db이름(연결할 mysql schema)
spring.datasource.username=root
spring.datasource.password=password(해당 username의 password)
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# MyBatis 설정 (기본적으로 사용 가능)
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.config-location=classpath:mybatis-config.xml

자동 설정이 적용되며, MyBatis의 SqlSessionFactory와 Mapper가 Spring Boot의 IoC 컨테이너에 의해 자동으로 설정되고 관리됨.

spring.datasource.* : 데이터베이스 연결 설정.
mybatis.mapper-locations: MyBatis 매퍼 XML 파일의 위치를 지정.
mybatis.config-location=classpath:mybatis-config.xml: MyBatis 설정 xml 파일의 위치를 지정.

User 테이블 생성

CREATE TABLE `mybatis`.`users` (
  `id` BIGINT AUTO_INCREMENT NOT NULL PRIMARY KEY,
  `name` VARCHAR(45) NOT NULL,
  `email` VARCHAR(45) NOT NULL,
  );

User.java

package com.ex.user.dto;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class User {
	private Long id;
	private String name;
	private String email;
}


@Getter
@Setter
@ToString 와 같은 Lombok 어노테이션은 사용하는 개발툴(sts, vscode)에 따라 Lombok 플러그인을 설치하여야 함.

Mapper 인터페이스 정의

데이터베이스의 테이블에 대응하는 Mapper 인터페이스를 작성함.
이 인터페이스는 MyBatis가 관리하는 Mapper XML 파일 또는 어노테이션을 통해 SQL 쿼리와 연결됨.

UserMapper.java

package com.ex.user.mapper;

import org.apache.ibatis.annotations.Mapper;
import com.ex.user.dto.User;

@Mapper
public interface UserMapper {
    User selectMemberById(Long id);
    void insertMember(User member);
}

mybatis-config 설정 파일 작성

mybatis-config.xml 파일을 통해 MyBatis의 설정을 정의함.

파일위치 = src/main/resources/mybatis-config.xml

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<!-- 스네이크 케이스를 카멜 케이스로 매핑. 해당 예시에서는 사용 X -->
    <settings>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <typeAliases>
        <!--UserMapper에서 타입을 명시한 alias로 사용하기위함
        가독성 향상, 유지보수 용이성, 타이핑 오류 감소 장점
        전체 클래스 경로 com.ex.user.dto.User를 반복해서 작성할 필요가 없어짐-->
        <typeAlias type="com.ex.user.dto.User" alias="user"></typeAlias>
        
    </typeAliases>
</configuration>

mapUnderscoreToCamelCase 예)

CREATE TABLE users (
    user_id INT PRIMARY KEY,
    user_name VARCHAR(50)
);
public class User {
    private int userId;    // 카멜 케이스
    private String userName;
    
}

Mapper XML 파일 작성

SQL 문과 매핑 정보를 담은 XML 파일을 작성함.
Mapper 인터페이스의 메소드와 동일한 아이디로 SQL 문을 정의함.

파일위치 = src/main/resources/mapper/UserMapper.xml

UserMapper.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 이 매퍼 파일이 UserMapper라는 인터페이스와 연관된 매퍼임을 명시 -->
<mapper namespace="com.ex.user.mapper.UserMapper">
  								<!-- com.ex.user.dto.User 대신 user -->
    <select id="selectUserById" parameterType="Long" resultType="user">
        SELECT * FROM user WHERE id = #{id}
    </select>
  						<!-- com.ex.user.dto.User 대신 user -->
    <select id="findAll" resultType="user">
        SELECT id, name, email from user order by id desc
    </select>
							<!-- com.ex.user.dto.User 대신 user -->
    <insert id="insertUser" parameterType="user">
        INSERT INTO user (name, email) VALUES (#{name}, #{email})
    </insert>
</mapper>

UserService 작성

UserService.java

package com.ex.user.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.ex.user.dto.User;
import com.ex.user.mapper.UserMapper;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class UserService {
	
	private final UserMapper userMapper;
	
	public void insertUser(User user) {
		userMapper.insertUser(user);
	}
	
	public User selectUserById(Long id) {
		return userMapper.selectUserById(id);
	}

	public List<User> findAll() {
		return userMapper.findAll();
	}
	
}

thymeleaf를 사용한 html(VIEW) 작성

파일 위치 = src/main/resources/templates/*

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<a href="/save">회원가입</a>
<a href="/userlist">회원 리스트</a>
</body>
</html>

save.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/save" method="post">
    이름: <input type="text" name="name"><br>
    이메일: <input type="text" name="email"><br>
    <input type="submit" value="회원가입">
</form>
</body>
</html>

userinfo.html

thymeleaf(타임리프) 사용을 위해서 html태그 안에 xmlns:th="http://www.thymeleaf.org"를 작성해줘야 함.

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>userinfo</title>
    <style>
        table, tr, td, th {
            border: 1px solid black;
            border-collapse: collapse;
        }
        th, td {
            padding: 10px;
        }
    </style>
</head>
<body>
<table>
    <tr>
        <th>name</th>
        <td th:text="${user.name}"></td>
    </tr>
    <tr>
        <th>email</th>
        <td th:text="${user.email}"></td>
    </tr>
</table>
</body>
</html>

userlist.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        table, tr, td, th {
            border: 1px solid black;
            border-collapse: collapse;
        }
        th, td {
            padding: 10px;
        }
    </style>
<body>
<a href="/save">회원가입</a>
<table>
    <tr>
        <th>이름</th>
        <th>이메일</th>
    </tr>
    <tr th:each="user: ${userList}">
        <td>
            <a th:text="${user.name}" th:href="@{|/userinfo?id=${user.id}|}"></a>
        </td>
        <td th:text="${user.email}"></td>
    </tr>
</table>
</body>
</html>

UserController 작성

UserController.java

package com.ex.user.controller;

import java.io.IOException;
import java.util.List;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import com.ex.user.dto.User;
import com.ex.user.service.UserService;

import lombok.RequiredArgsConstructor;

@Controller
@RequiredArgsConstructor
public class UserController {
	
	private final UserService userService;
	
	@GetMapping("/")
    public String index() {
        return "index";
    }
	
	@GetMapping("/save")
    public String insertUser() {
        return "save";
    }
	
	@PostMapping("/save")
    public String save(@ModelAttribute User userDTO) throws IOException {
        System.out.println("userDTO = " + userDTO);
        userService.insertUser(userDTO);
        return "index";
    }
	
	@GetMapping("/userinfo")
    public String userinfo(@RequestParam("id") Long id, Model model) {
		User user = userService.selectUserById(id);
		model.addAttribute("user", user);
        return "userinfo";
    }
	
	@GetMapping("/userlist")
    public String userList(Model model) {
		List<User> userList = userService.findAll();
        model.addAttribute("userList", userList);
        System.out.println("userList = " + userList);
        return "userlist";
    }
	
}

MyBatis ResultMap


MyBatis의 ResultMap은 복잡한 객체 매핑을 지원하는 기능으로, 데이터베이스에서 조회한 결과를 객체로 변환할 때 사용됨. 특히, 테이블 간의 관계가 있을 때는 association과 collection을 사용하여 1:1 및 1:N관계를 객체로 매핑할 수 있음.

MyBatis ResultMap 사용 예시

데이터베이스 테이블 예시

Order 테이블

id: Order의 고유 ID (Primary Key)
order_number: 주문 번호

CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_number VARCHAR(255)
);

Product 테이블

id: Product의 고유 ID (Primary Key)
name: 제품 이름
price: 제품 가격

CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255),
    price DECIMAL(10, 2)
);

OrderItem 테이블

id: OrderItem의 고유 ID (Primary Key)
order_id: Order와 연결된 외래 키 (Foreign Key)
product_id: Product와 연결된 외래 키 (Foreign Key)
quantity: 주문한 수량

CREATE TABLE order_items (
    id INT PRIMARY KEY AUTO_INCREMENT,
    order_id INT,
    product_id INT,
    quantity INT,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY (product_id) REFERENCES products(id)
);

Order와 OrderItem은 1:N관계
OrderItem과 Product는 1:1 관계

자바 객체 모델

//Order 클래스
public class Order {
    private int id;
    private String orderNumber;
    private List<OrderItem> orderItems;  // 1:N 관계
}
//OrderItem 클래스
public class OrderItem {
    private int id;
    private int orderId;
    private int quantity;
    private Product product;  // 1:1 관계 (OrderItem과 Product)

}
//Product 클래스
public class Product {
    private int id;
    private String name;
    private double price;
}

MyBatis Mapper 및 ResultMap 정의

MyBatis에서 복잡한 관계를 매핑하기 위해 ResultMap과 함께 association과 collection을 사용합니다. association은 1:1 관계를 나타내고, collection은 1:N관계를 나타냄.

<!-- OrderMapper.xml -->
<mapper namespace="com.ex.mapper.OrderMapper">

    <!-- Order와 OrderItem의 1:N 매핑 -->
    <resultMap id="orderResultMap" type="com.ex.model.Order">
        <id property="id" column="order_id"/>
        <result property="orderNumber" column="order_number"/>

        <!-- 1:N 관계: Order와 OrderItems -->
        <collection property="orderItems" ofType="com.ex.dto.OrderItem">
            <id property="id" column="order_item_id"/>
            <result property="quantity" column="quantity"/>

            <!-- 1:1 관계: OrderItem과 Product -->
            <association property="product" javaType="com.ex.dto.Product">
                <id property="id" column="product_id"/>
                <result property="name" column="product_name"/>
                <result property="price" column="product_price"/>
            </association>
        </collection>
    </resultMap>

    <!-- Order 조회 SQL -->
    <select id="selectOrderById" resultMap="orderResultMap">
        SELECT 
            o.id AS order_id,
            o.order_number,
            oi.id AS order_item_id,
            oi.quantity,
            p.id AS product_id,
            p.name AS product_name,
            p.price AS product_price
        FROM orders o
        JOIN order_items oi ON o.id = oi.order_id
        JOIN products p ON oi.product_id = p.id
        WHERE o.id = #{id}
    </select>
  
	<insert id="insertOrder" parameterType="com.ex.dto.Order" useGeneratedKeys="true" keyProperty="id">
	    INSERT INTO orders (order_number)
	    VALUES (#{orderNumber})
	</insert>
	
	<insert id="insertOrderItems" parameterType="com.ex.dto.OrderItem">
	    INSERT INTO order_items (order_id, product_id, quantity)
	    VALUES (#{orderId}, #{product.id}, #{quantity})
	</insert>
</mapper>

<resultMap id="orderResultMap" type="com.ex.dto.Order">

resultMap은 SQL 쿼리 결과를 특정 자바 객체(여기서는 Order 객체)에 매핑하는 규칙을 정의합니다.
type="com.ex.dto.Order"는 SQL 결과를 Order 객체에 매핑하겠다는 뜻입니다.
id="orderResultMap"은 이후 SQL 쿼리에서 이 매핑 규칙을 참조하기 위한 식별자입니다.


<id property="id" column="order_id"/>

Order 객체의 id필드에 데이터베이스의 order_id 컬럼 값을 매핑합니다. id는 이 객체의 식별자로 설정됩니다.


<result property="orderNumber" column="order_number"/>

Order 객체의 orderNumber 속성에 데이터베이스의 order_number 컬럼 값을 매핑합니다. 이는 주문 번호를 의미합니다.


<collection property="orderItems" ofType="com.ex.dto.OrderItem">

collection은 1:N관계에서 사용되며, 하나의 Order가 여러 OrderItem을 가질 수 있음을 나타냅니다.
property="orderItems"Order 객체가 가지고 있는 orderItems라는 속성에 매핑된다는 의미입니다. 즉, 여러 개의 OrderItem이 이 속성에 매핑됩니다.
ofType="com.ex.dto.OrderItem"는 컬렉션 내의 각 객체가 OrderItem 타입임을 나타냅니다.
collection 태그 안에 OrderItem에 대한 매핑 규칙을 정의합니다.


<id property="id" column="order_item_id"/>

OrderItem 객체의 id 속성에 데이터베이스의 order_item_id 값을 매핑합니다. 이는 OrderItem의 식별자로 사용됩니다.
<result property="quantity" column="quantity"/>
OrderItem 객체의 quantity 속성에 데이터베이스의 quantity 값을 매핑합니다. 이는 해당 주문 항목의 수량을 의미합니다.


<association property="product" javaType="com.ex.dto.Product">

association은 1:1 관계에서 사용됩니다. 여기서는 각 OrderItem이 하나의 Product와 연결되어 있다는 것을 나타냅니다.
property="product"OrderItem 객체가 가지고 있는 product라는 속성에 매핑된다는 의미입니다.
javaType="com.ex.dto.Product"는 연관된 객체가 Product 타입임을 나타냅니다.


<id property="id" column="product_id"/>

Product 객체의 id 속성에 데이터베이스의 product_id 값을 매핑합니다. 이는 Product 객체의 식별자로 사용됩니다


<result property="name" column="product_name"/>

Product 객체의 name 속성에 데이터베이스의 product_name 값을 매핑합니다. 이는 상품의 이름을 의미합니다.


<result property="price" column="product_price"/>

Product 객체의 price 속성에 데이터베이스의 product_price 값을 매핑합니다. 이는 상품의 가격을 의미합니다.

Mapper 인터페이스

@Mapper
public interface OrderMapper {
	
	// Order 객체를 반환
	Order selectOrderById(int id);  
	
	// Order 삽입
    void insertOrder(Order order);

    // OrderItem 삽입
    void insertOrderItems(OrderItem orderItem);
}

Service 클래스

@Service
@RequiredArgsConstructor
public class OrderService {
	    private final OrderMapper orderMapper;

	    @Transactional
	    public Order createOrder(Order order) {
	        // 1. Order 삽입
	        orderMapper.insertOrder(order);

	        // 2. OrderItems 삽입 (Order의 ID가 사용됨)
	        for (OrderItem orderItem : order.getOrderItems()) {
	            orderItem.setOrderId(order.getId()); // Order ID 설정
	            orderMapper.insertOrderItems(orderItem);
	        }
	        
	        return order;
	    }
}

Order 삽입

insertOrder 메서드를 호출하여 Order 테이블에 새 주문을 삽입합니다.
useGeneratedKeys 옵션을 통해 Order의 ID가 자동으로 생성되며, 이 ID가 Order 객체의 id 필드에 설정됩니다.

OrderItem 삽입

OrderItem에 대해 반복문을 통해 각 주문 항목을 insertOrderItems 메서드로 삽입합니다.
이때 각 OrderItem의 orderId는 삽입된 Order의 ID를 참조합니다.

트랜잭션

@Transactional 어노테이션을 사용하여 하나의 트랜잭션 내에서 Order와 OrderItem 삽입을 처리합니다. 만약 삽입 과정 중 오류가 발생하면 전체 작업이 롤백됩니다.

MyBatis 동적 SQL


MyBatis 동적 SQL은 조건에 따라 SQL을 동적으로 생성할 수 있도록 해주는 기능임. 이는 일반적인 SQL 쿼리문을 고정적으로 사용하는 것이 아니라, 상황에 따라 SQL 문장을 동적으로 변경할 수 있게 함. 동적 SQL을 사용할 때는 XML 태그를 통해 제어할 수 있음.

동적 SQL을 사용하는 이유는 SQL 쿼리의 조건부 실행을 효율적으로 처리하기 위해서임. 예를 들어, 검색 조건에 따라 다르게 동작하는 WHERE 절을 만들거나, INSERT문에서 조건에 따라 일부 컬럼만 값을 넣는 등의 상황에서 매우 유용함.

동적 SQL 사용 예시

1. <if> 태그

설명: if 태그는 주어진 조건이 참일 때만 해당 SQL 문을 포함함.

용도: 특정 조건에 따라 SQL 문을 추가하거나 제외할 때 사용함.

<select id="findUserByIdAndName" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <if test="id != null">
        AND id = #{id}
    </if>
    <if test="name != null">
        AND name = #{name}
    </if>
</select>

id와 name이 각각 null이 아닐 때만 해당 조건이 SQL에 추가됨.
id와 name 중 하나라도 있을 때 해당 조건을 추가하여 검색할 수 있음.

2. <choose>, <when>, <otherwise> 태그

설명: choose는 if-else 구문과 유사함. 여러 조건 중에서 첫 번째로 참인 조건만 실행됨. when은 조건이 참일 때 실행되고, otherwise는 어떤 when도 참이 아닐 때 실행됨.
용도: 여러 조건 중 하나만 만족할 때 하나의 SQL을 실행하고, 기본 값을 설정할 때 유용함.

<select id="findUserByCondition" resultType="User">
    SELECT * FROM users
    WHERE 1=1
    <choose>
        <when test="id != null">
            AND id = #{id}
        </when>
        <when test="name != null">
            AND name = #{name}
        </when>
        <otherwise>
            AND age > 30
        </otherwise>
    </choose>
</select>

설명: id가 있으면 id 조건을, 없으면 name 조건을 사용함. 둘 다 없으면 age > 30 조건을 사용함.
사용 예시: 여러 조건 중 하나만 선택해서 SQL을 실행할 때 사용함.

3. <where> 태그

설명: where 태그는 자동으로 WHERE 절을 처리해 주고, 불필요한 AND 또는 OR 연산자를 제거해줌.
용도: 동적으로 WHERE 절의 조건을 추가할 때 사용함.

<select id="findUsers" resultType="User">
    SELECT * FROM users
    <where>
        <if test="id != null">
            id = #{id}
        </if>
        <if test="name != null">
            AND name = #{name}
        </if>
    </where>
</select>

설명: where 태그는 내부적으로 첫 번째 조건 앞에 WHERE를 추가해주고, 두 번째 조건부터는 AND를 자동으로 붙여줌.
사용 예시: 여러 조건을 동적으로 추가하면서 SQL의 구문 오류를 방지하고자 할 때 사용함.

4. <set> 태그

설명: set 태그는 UPDATE 문에서 SET 절을 동적으로 생성할 때 사용함. 불필요한 쉼표를 자동으로 처리해줌.
용도: UPDATE 문에서 동적으로 컬럼을 수정할 때 사용함.

<update id="updateUser" parameterType="User">
    UPDATE users
    <set>
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null">
            email = #{email}
        </if>
    </set>
    WHERE id = #{id}
</update>

설명: set 태그는 마지막에 불필요한 쉼표를 자동으로 제거해 줌.
사용 예시: 업데이트할 필드가 조건에 따라 동적으로 변경될 때 사용함.

5. <trim> 태그

설명: trim 태그는 SQL 구문을 제어하여, 접두사(prefix)접미사(suffix)를 동적으로 추가하거나 제거할 수 있음.
용도: WHERE나 SET 절에서 조건에 따라 접두사나 접미사를 제어할 때 사용함.

<update id="updateUser" parameterType="User">
    UPDATE users
    <trim prefix="SET" suffixOverrides=",">
        <if test="name != null">
            name = #{name},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="email != null">
            email = #{email}
        </if>
    </trim>
    WHERE id = #{id}
</update>

설명: prefix="SET"는 SET 구문을 자동으로 추가해주고, suffixOverrides=","는 마지막 쉼표를 제거해줌.
사용 예시: SQL 쿼리의 구조적 제어가 필요할 때 사용함. 특히, SET 절에서 여러 필드를 동적으로 설정하면서 마지막 쉼표를 자동으로 제거할 수 있음.

6. <foreach> 태그

설명: foreach는 리스트나 배열 등의 반복 가능한 데이터에 대해 반복적으로 SQL 문을 생성할 때 사용함.
용도: IN 절이나 다중 INSERT와 같은 반복 SQL 문을 동적으로 생성할 때 유용함.

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

설명:
collection="idList": idList라는 파라미터가 배열이나 리스트 형태로 들어옴.
item="id": idList의 각 요소가 id로 매핑됨.
open="(", separator=",", close=")": 반복된 요소를 괄호로 감싸고, 요소 간에 쉼표로 구분함.
사용 예시: 여러 개의 ID를 한 번에 받아서 IN 절을 통해 다중 데이터를 조회할 때 사용함.
또한, foreach는 다중 INSERT에서도 유용하게 사용됨.

<insert id="insertUsers">
    INSERT INTO users (name, age, email)
    VALUES
    <foreach collection="userList" item="user" separator=",">
        (#{user.name}, #{user.age}, #{user.email})
    </foreach>
</insert>

설명: userList라는 리스트의 각 요소를 user로 받아서 다중 행 INSERT를 처리함.
사용 예시: 여러 개의 데이터를 한 번에 삽입할 때 유용함.

7. <bind> 태그

설명: bind 태그는 SQL 문에서 동적으로 변수를 생성하여 사용할 수 있게 해줌. 이는 SQL 문 안에서 표현식을 동적으로 평가하여 사용할 때 유용함.
용도: 동적인 값이나 파라미터를 SQL에 반영해야 할 때 사용함.

<select id="findUserByName" resultType="User">
    <bind name="pattern" value="'%' + name + '%'" />
    SELECT * FROM users
    WHERE name LIKE #{pattern}
</select>

설명: bind 태그는 name 파라미터 앞뒤에 %를 붙여 LIKE 검색에 사용할 수 있게 함.
사용 예시: 문자열 조작이나 동적 조건을 처리할 때 유용함.

MyBatis의 장점


SQL 제어의 유연성

개발자가 직접 SQL을 작성하므로, 복잡한 쿼리나 최적화된 쿼리를 구현하기 쉬움.
데이터베이스 벤더(오라클(Oracle),MySQL,PostgreSQL)에 특화된 기능도 사용할 수 있음.

간단한 설정과 사용법

설정이 비교적 간단하여 빠르게 학습하고 적용할 수 있음.
JDBC의 반복적인 코드를 줄여주어 개발 효율을 높임.

동적 SQL의 편의성

동적 SQL을 쉽게 작성할 수 있어 다양한 조건에 대응하는 쿼리를 효율적으로 관리할 수 있음.

MyBatis의 단점


SQL 관리의 복잡성

SQL을 직접 작성하므로, 쿼리가 많아질수록 관리 부담이 증가함.
SQL 문과 자바 코드가 분리되어 있어 유지보수가 어려울 수 있음.

객체와 관계형 데이터베이스 간의 불일치

MyBatis는 ORM이 아니기 때문에, 객체와 데이터베이스 간의 매핑을 자동으로 처리하지 않음.
복잡한 객체 그래프를 처리하기 위해서는 추가적인 매핑 작업이 필요함.

벤더 종속성

SQL을 직접 작성하기 때문에 데이터베이스 벤더(오라클(Oracle),MySQL,PostgreSQL)에 종속적인 쿼리가 많아질 수 있음.

MyBatis vs Hibernate(JPA)


MyBatis는 SQL을 직접 작성하여 유연성과 제어권을 높인 반면, Hibernate(JPA)는 객체와 테이블 간의 매핑을 자동화하여 개발 생산성을 높임.
복잡한 쿼리가 많고 SQL 최적화가 중요한 프로젝트에서는 MyBatis가 유리하며, 데이터 모델이 복잡하고 객체 지향적인 매핑이 중요한 경우 Hibernate(JPA)가 적합함.

MyBatis의 사용 사례


기존 데이터베이스 활용: 이미 구축된 데이터베이스에 복잡한 SQL 쿼리가 많을 경우.
성능 최적화 필요 시: SQL 튜닝이 필요하여 직접적인 제어가 필요한 경우.
단순한 매핑 요구: 복잡한 ORM 기능이 필요하지 않고, 간단한 매핑만 필요한 경우.

결론


MyBatis는 SQL 제어권을 유지하면서도 개발 생산성을 높이고자 하는 개발자에게 적합한 프레임워크임. JDBC의 번거로움을 줄여주고, 동적 SQL과 매핑 기능을 제공하여 효율적인 데이터베이스 연동을 가능하게 함. 하지만 SQL 관리의 부담과 객체-관계 불일치 문제를 고려하여 프로젝트의 특성에 맞게 선택하는 것이 중요함.

profile
웹개발자 취업 준비생

0개의 댓글