Skip to content

JAVACAFE-STUDY/mybatis-sample

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Spring boot์— MyBatis ์„ค์ •ํ•˜๊ธฐ

์Šคํ”„๋ง ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ• ๋•Œ๋งˆ๋‹ค DB์™€์˜ ์—ฐ๋™์€ ํ•„์ˆ˜์ ์œผ๋กœ ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค. ์ตœ๊ทผ์—๋Š”(๋“œ๋””์–ด) JPA๋ฅผ ์ด์šฉํ•ด ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŽ์ด ์ง„ํ–‰ํ•˜์ง€๋งŒ, ์—ฌ์ „ํžˆ SQL์„ ์ด์šฉํ•œ ์ง๊ด€์ ์ธ ๋งคํ•‘๋ฐฉ์‹๋„ ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค. ๋‹จ์ˆœํ•œ ํ”„๋กœ์ ํŠธ๋ผ๋ฉด SpringJdbc๋ฅผ ์ด์šฉํ•ด์„œ๋„ ์ถฉ๋ถ„ํžˆ SQL์„ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ ์‹ค๋ฌดํ™˜๊ฒฝ์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ์˜์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•˜๊ณ  ์ด์— ๋”ฐ๋ฅธ SQL๋„ ํ•จ๊ป˜ ๋ณ€๊ฒฝ๋˜์–ด์•ผ ํ•˜๊ณ , ์ด๋Ÿฌํ•œ ๋ณต์žกํ•œ SQL์ด ํ•„์š”ํ•œ ์ง€์ ์—์„œ MyBatis์˜ ๋™์  SQL๊ด€๋ฆฌ๋ฐฉ๋ฒ•์€ ํ”„๋กœ์ ํŠธ์˜ ๋ณต์žก๋„๋ฅผ ์ค„์ด๋Š”๋ฐ ํฐ ๋„์›€์ด ๋œ๋‹ค.

์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” spring boot๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•˜๊ฒŒ MyBatis ์ƒ˜ํ”Œ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฒ•์„ ์•Œ์•„๋ณด๊ณ , ๋””๋ฒ„๊น…์„ ์œ„ํ•œ SQL์„ค์ • ๋ฐ ํ™•์žฅํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๊ณต์œ ํ•˜๊ณ ์ž ํ•œ๋‹ค.

ํ”„๋กœ์ ํŠธ ์„ค์ •ํ•˜๊ธฐ

Spring Boot ํ”„๋กœ์ ํŠธ ์ƒ์„ฑํ•˜๊ธฐ

MyBatis ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ spring boot์šฉ์œผ๋กœ ์ œ๊ณต๋˜๊ณ  ์žˆ๋‹ค. ์ด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ„๋‹จํ•˜๊ฒŒ mybatis์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

https://start.spring.io ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ํ”„๋กœ์ ํŠธ ๋ผˆ๋Œ€๋ฅผ ๋งŒ๋“ค์–ด์ฃผ์ž

์ƒ์„ฑ๋œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด pom.xml์ด ๋งŒ๋“ค์–ด์ง„๋‹ค.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>net.chndol.study</groupId>
    <artifactId>mybatis-sample</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>mybatis-sample</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>
  • mybatis-spring-boot-starter๋ฅผ ํ†ตํ•ด์„œ MyBatis ์˜์กด๊ด€๊ณ„๊ฐ€ ๋“ค์–ด๊ฐ€๊ฒŒ ๋˜๋ฉฐ Spring ๊ด€๋ จ ๊ธฐ๋ณธ์„ค์ •์ด ๋“ค์–ด๊ฐ€๊ฒŒ ๋œ๋‹ค.
  • h2๋ฅผ ์ด์šฉํ•˜์˜€๋‹ค. inmemory db๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ ๋™์ž‘ํ•˜๋Š” ํ…Œ์ŠคํŠธ์šฉ๋„ DB์ด๋‹ค. ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๋Š” ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด ์™ธ๋ถ€ ๋””๋น„ ๋งคํ•‘์—†์ด ๋Œ์•„๊ฐ€๋„๋ก ์„ค์ •ํ•˜์˜€๋‹ค.

H2 Datasource์„ค์ • ๋ฐ DBํ…Œ์ด๋ธ” ๊ตฌ์„ฑํ•˜๊ธฐ

H2๋„ ๊ธฐ์กด์˜ Database์™€ ์œ ์‚ฌํ•œ๋ฐฉ์‹์œผ๋กœ DB์„ค์ •์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

main/resources/application.properties์— ์•„๋ž˜์™€ ๊ฐ™์ด url, username, password๋ฅผ ์„ค์ •ํ•˜์ž.

spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.datasource.driver-class-name=org.h2.Driver

๊ทธ๋ฆฌ๊ณ  ์˜ˆ์ œ์— ์‚ฌ์šฉํ•  City๋ผ๋Š” ํ…Œ์ด๋ธ”์„ ๋งŒ๋“ค์–ด์ฃผ๋„๋ก ํ•˜์ž. ํ•ด๋‹น ํ…Œ์ด๋ธ”์—๋Š” ID, NAME(๋„์‹œ๋ช…), COUNTRY(๊ตญ๊ฐ€), POPULATION(์ธ๊ตฌ์ˆ˜) ์ปฌ๋Ÿผ์„ ๊ฐ€์ง„๋‹ค.

H2๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ DB๋กœ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ๋™์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ์—๋งŒ DB๊ฐ€ ํ•จ๊ป˜ ๋™์ž‘ํ•œ๋‹ค. Spring์ด ์‹œ์ž‘ํ•˜๋Š” ์‹œ์ ์— scheme์ด ์ƒ์„ฑ๋˜๋„๋ก /src/main/resource/scheme.sql์„ ์ƒ์„ฑํ•˜์—ฌ ์ฃผ๊ณ  ์•„๋ž˜์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด table ์ƒ์„ฑ SQL๊ณผ ์˜ˆ์ œ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฃผ์ž.

DROP TABLE IF EXISTS CITY;

CREATE TABLE CITY (ID INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR, COUNTRY VARCHAR, POPULATION INT);

INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES ('San Francisco', 'US', 10000);
INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES ('์„œ์šธ', 'KR', 20000);
INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES ('ๆฑไบฌ', 'JP', 30000);
INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES ('๋ถ€์‚ฐ', 'KR', 40000);

scheme.sql์„ ํ™œ์šฉํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ดˆ๊ธฐํ™”์™€ ๊ด€๋ จ๋œ ์ž์„ธํ•œ ์„ค์ •์€ ํ•ด๋‹น ์Šคํ”„๋ง ๋ฌธ์„œ ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

MyBatis ์„ค์ •ํ•˜๊ธฐ

mapper๋ฅผ ์Šค์บ”ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค์ •ํ•˜์ž. SpringBoot Configuration ํŒŒ์ผ์ƒ๋‹จ์— ์•„๋ž˜์™€ ๊ฐ™์ด ํ”„๋กœ์ ํŠธ ํŒจํ‚ค์ง€์˜ ๊ฒฝ๋กœ๋ฅผ ๋„ฃ์ž. ์ด๋ ‡๊ฒŒ ํ•˜์—ฌ ํ”„๋กœ์ ํŠธ ํ•˜์œ„์— ์กด์žฌํ•˜๋Š” Mapper๋“ค์„ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ• ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.

import org.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan(basePackages = "net.chandol.study.mybatissample")
public class MybatisSampleApplication {
....

main/resources/application.properties์—๋„ ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜์ž. type์„ ์‰ฝ๊ฒŒ ์“ฐ๊ธฐ ์œ„ํ•ด์„œ model ํŒจํ‚ค์ง€๋ฅผ type-aliaes์— ์„ค์ •ํ•˜์˜€๋‹ค. ๊ทธ๋ฆฌ๊ณ  DB ๋กœ๊น…์„ ์œ„ํ•ด์„œ mapper ํŒจํ‚ค์ง€๋กœ๊น…์„ TRACE๋กœ ์„ค์ •ํ•˜์˜€๋‹ค.

# mybatis ๋งคํ•‘ type์„ ์งง๊ฒŒ ์“ฐ๊ธฐ ์œ„ํ•œ ์„ค์ •
mybatis.type-aliases-package=net.chndol.study.mybatissample.model

# mapper ์ดํ•˜๋ฅผ ๋กœ๊น… ์œ„์น˜๋กœ ์„ค์ •.
logging.level.net.chndol.study.mybatissample.mapper=TRACE

ํ”„๋กœ์ ํŠธ๋ฅผ ์‹œ์ž‘ํ•˜๋Š”๋ฐ ์ด์ •๋„๋ฉด ์ถฉ๋ถ„ํžˆ ์“ธ๋งŒํ•  ๊ฒƒ์ด๋‹ค. spring boot starter๋กœ ์„ค์ •๋œ mybatis๋ฅผ ์‚ฌ์šฉํ•˜์˜€์œผ๋ฏ€๋กœ, ๊ธฐ๋ณธ์ ์œผ๋กœ ํ•„์š”ํ•œ ์„ค์ •๊ฐ’๋“ค์€ AutoConfigure๋ฅผ ํ†ตํ•ด์„œ ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋œ๋‹ค. mybatis์™€ ์—ฐ๊ด€๋œ ์ข€ ๋” ์ž์„ธํ•œ ์„ค์ •์€ ์—ฌ๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜๋ฉด ์นœ์ ˆํ•˜๊ฒŒ ์•ˆ๋‚ด๋˜์–ด์žˆ๋‹ค.

์ฝ”๋“œ ์ž‘์„ฑํ•ด๋ณด๊ธฐ

Model, Mapper, XML ๊ตฌ์„ฑํ•˜๊ธฐ

์ด์ œ ์œ„์—์„œ ๋งŒ๋“ค์–ด๋†“์€ CITYํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” model์„ ์„ค์ •ํ•˜์—ฌ ๋ณด์ž

package net.chndol.study.mybatissample.model;

@Data
@Alias("city")
public class City {
    private Long id;
    private String name;
    private String country;
    private Long population;

    public City() {
    }

    public City(String name, String country, Long population) {
        this.name = name;
        this.country = country;
        this.population = population;
    }
}

๊ทธ๋ฆฌ๊ณ  ํ•ด๋‹น Model๊ณผ ์—ฐ๊ฒฐ๋  Mapper๋ฅผ ๊ตฌ์„ฑํ•˜์—ฌ ์ฃผ์ž.

์šฐ์„  mapper interface๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌ์„ฑํ•˜์ž.

package net.chndol.study.mybatissample.repository;

import net.chndol.study.mybatissample.model.City;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface CityMapper {
    City selectCityById(Long id);
    List<City> selectAllCity();
    void insertCity(City city);
}

๊ทธ๋ฆฌ๊ณ  SQL์„ ๊ฐ€์ง„ ํ•ต์‹ฌ XML์€ ์•„๋ž˜์™€ ๊ฐ™์ด ๋งŒ๋“ค์–ด ์ฃผ์ž. ํ•ด๋‹น ํŒŒ์ผ์„ resource ํ•˜์œ„์— mapper ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋™์ผํ•œ ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ ํ•˜์œ„์— ๋„ฃ์œผ๋ฉด ๋œ๋‹ค.

<?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">

<mapper namespace="net.chndol.study.mybatissample.repository.CityMapper">

    <select id="selectCityById" resultType="city">
        SELECT ID
              ,NAME
              ,COUNTRY
              ,POPULATION
          FROM CITY
         WHERE ID = #{id}
    </select>

    <select id="selectAllCity" resultType="city">
        SELECT ID
              ,NAME
              ,COUNTRY
              ,POPULATION
          FROM CITY
    </select>

    <insert id="insertCity">
      INSERT INTO CITY (NAME, COUNTRY, POPULATION)
      VALUES (#{name}, #{country}, #{population})
    </insert>

</mapper>

MyBatis๋Š” ์ด์™ธ์—๋„ mapper annotation์„ ์ด์šฉํ•ด SQL๋ฌธ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•˜์ง€๋งŒ MyBatis๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฐ€์žฅ ํฐ ์ด์œ ์ค‘ ํ•˜๋‚˜์ธ XML์„ ๊ธฐ๋ฐ˜์œผ๋กœ SQL์„ ๊น”๋”ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์žฅ์ ์ด ์—†์–ด์ง€๋Š”๊ฒƒ ๊ฐ™์•„์„œ ๊ฐœ์ธ์ ์œผ๋กœ ์„ ํ˜ธํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค. ํ•ด๋‹น ๋ฐฉ์‹์— ๋Œ€ํ•œ ์ฝ”๋“œ๋Š” ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜๋ผ.

๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ service์™€ test๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

@Service
@Transactional
public class CityService {
    @Autowired
    CityMapper cityMapper;

    public City getCityById(Long id) {
        return cityMapper.selectCityById(id);
    }

    public List<City> getAllCity() {
        return cityMapper.selectAllCity();
    }

    public void addCity(City city) {
        cityMapper.insertCity(city);
    }
}
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
@Transactional
public class CityServiceTest {
    @Autowired
    CityService service;

    @Test
    public void getCityById() {
        City city = service.getCityById(1L);
        log.info("city : {}", city);
    }

    @Test
    public void getAllCity() {
        List<City> cities = service.getAllCity();
        log.info("cities : {}", cities);
    }


    @Test
    public void addCities() {
        service.addCity(new City("๋‰ด์š•", "๋ฏธ๊ตญ", 1_000_000L));
        service.addCity(new City("๋Ÿฐ๋˜", "์˜๊ตญ", 2_000_000L));
        service.addCity(new City("ํŒŒ๋ฆฌ", "ํ”„๋ž‘์Šค", 3_000_000L));
    }

}

ํ•ด๋‹น ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰์‹œ์ผœ๋ณด๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋กœ๊ทธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค. SQL์ด ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ–ˆ๋Š”์ง€, paramter๊ฐ€ ๋ฌด์—‡์ธ์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

2018-07-30 12:21:44.371 DEBUG 25521 --- [           main] n.c.s.m.m.CityMapper.selectCityById      : ==>  Preparing: SELECT ID ,NAME ,COUNTRY ,POPULATION FROM CITY WHERE ID = ? 
2018-07-30 12:21:44.390 DEBUG 25521 --- [           main] n.c.s.m.m.CityMapper.selectCityById      : ==> Parameters: 1(Long)
2018-07-30 12:21:44.407 TRACE 25521 --- [           main] n.c.s.m.m.CityMapper.selectCityById      : <==    Columns: ID, NAME, COUNTRY, POPULATION
2018-07-30 12:21:44.408 TRACE 25521 --- [           main] n.c.s.m.m.CityMapper.selectCityById      : <==        Row: 1, San Francisco, US, 10000
2018-07-30 12:21:44.412 DEBUG 25521 --- [           main] n.c.s.m.m.CityMapper.selectCityById      : <==      Total: 1
2018-07-30 12:21:44.413  INFO 25521 --- [           main] n.c.s.m.service.CityServiceTest          : city : City(id=1, name=San Francisco, country=US, population=10000)
2018-07-30 12:21:44.416  INFO 25521 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: 

ํ•œ๊ฑธ์Œ๋”!

XMLํŒŒ์ผ๊ณผ interface ํ•˜๋‚˜์˜ ํด๋”์—์„œ ๊ด€๋ฆฌํ•˜๊ธฐ

MyBatis๋ฅผ ์ด์šฉํ•  ๊ฒฝ์šฐ ์ „ํ†ต์ (?)์œผ๋กœ XML์„ ํ†ตํ•ด์„œ SQL๋ฌธ์„ ์„ค์ •ํ•œ๋‹ค. ํ•˜์ง€๋งŒ Java์ฝ”๋“œ์™€ xmlํŒŒ์ผ์ด 1:1๋กœ ๋งคํ•‘๋จ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ , ํด๋”๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ๊ณณ์— ์œ„์น˜ํ•ด ๋‘˜์„ ์„œ๋กœ ํ™•์ธํ•˜๊ธฐ์—” ๋ถˆํŽธํ•˜๋‹ค. ์ด์Šˆ๊ฐ€ ๋ฐœ์ƒํ• ๋•Œ๋งˆ๋‹ค . mapper interface ์™€ ๊ฐ™์€ ํด๋”์•ˆ์— ์žˆ์œผ๋ฉด ๋ณด๋‹ค ๋” ๊ด€๋ฆฌํ•˜๊ธฐ ํŽธ๋ฆฌํ•  ๊ฒƒ์ด๋‹ค.

ํ•˜์ง€๋งŒ xmlํŒŒ์ผ์„ java์™€ ๊ฐ™์€ ํด๋”๋กœ ์˜ฎ๊ธฐ๋”๋ผ๋„ ๋ฉ”์ด๋ธ ๋นŒ๋“œํƒ€์ž„์— javaํŒŒ์ผ์„ ์ œ์™ธํ•œ ๋‹ค๋ฅธ ํŒŒ์ผ๋“ค์€ ์˜ˆ์™ธ๋กœ ์„ค์ •๋˜์–ด ๋นŒ๋“œ๊ฒฝ๋กœ๋กœ ํŒŒ์ผ์„ ์˜ฎ๊ธฐ์ง€ ์•Š๋Š”๋‹ค. ๋•Œ๋ฌธ์— ๋นŒ๋“œ์‹œ xmlํŒŒ์ผ๋„ ๊ฐ™์ด ๋ณต์‚ฌ๋  ์ˆ˜ ์žˆ๋„๋ก ๋ฉ”์ด๋ธ ์„ค์ •์ด ํ•„์š”ํ•˜๋‹ค. ์•„๋ž˜์ฒ˜๋Ÿผ build ํƒœ๊ทธ ํ•˜์œ„์— resources ์„ค์ •์„ ์ถ”๊ฐ€ํ•˜์—ฌ ์ฃผ์ž.

<projec>
    ...    

    <build>
        <resources>
            <resource>
                <filtering>false</filtering>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
            </resource>
        </resources>

        ...
    </build>


</project>

์ด์ œ๋ถ€ํ„ฐ xml๊ณผ interface๋ฅผ ํ•˜๋‚˜์˜ ํด๋”์—์„œ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค!

๋กœ๊น…์„ ์ข€ ๋” ์นœ์ ˆํ•˜๊ฒŒ

์ง€๊ธˆ๊นŒ์ง€ ํ•ด์„œ ์ œ๋ฒ• ์“ธ๋งŒํ•œ ์„ค์ •๋“ค์„ ๋งŒ๋“ค์–ด๋†“์•˜๋‹ค. ๊ฐœ์ธ์ ์œผ๋กœ mybatis๋ฅผ ์ด์šฉํ•  ๋•Œ ๋ถˆํŽธํ–ˆ๋˜ ์ ์€ ๋™์  SQL๋กœ ๋ณ€ํ™”ํ•˜๊ณ  ๋‚œ ๋‹ค์Œ์— ์ƒ์„ฑ๋œ SQL๊ณผ ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋งคํ•‘๋˜๋Š” ๋ถ€๋ถ„์ด "?"๋กœ ์ „๋ถ€ ์น˜ํ™˜๋œ๋‹ค๋Š” ์ ์ด์˜€๋‹ค. ํ•ด๋‹น ๊ฐ’๋“ค์ด ์–ด๋–ค ์˜๋ฏธ์˜€๋Š”์ง€๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด์„  ๋‹ค์‹œ ํ•œ ๋ฒˆ xmlํŒŒ์ผ์„ ํ†ตํ•ด ํ™•์ธํ•˜์—ฌ์•ผ ํ•œ๋‹ค.

์ด๋Ÿฌํ•œ ๋‹จ์ ์„ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ถœ๋ ฅ๋˜๋Š” SQL์˜ ? ๋’ค์— ๋งคํ•‘ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ์ด๋ฆ„์„ ์ฝ”๋ฉ˜ํŠธ๋กœ ๋‚จ๊ฒจ๋ณด๋„๋ก ํ•˜์ž. ์ด๋ ‡๊ฒŒ ํ•  ๊ฒฝ์šฐ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋”๋ผ๋„ ๋น ๋ฅด๊ฒŒ ๋””๋ฒ„๊น…์ด ๊ฐ€๋Šฅํ•  ๊ฒƒ์ด๋‹ค.

MyBatis์˜ XML์—์„œ ์„ค์ •ํ•œ ๋™์  SQL๋ฌธ์€ XMLLanguageDriver๋ฅผ ํ†ตํ•ด์„œ JDBC์—์‚ฌ์šฉ๋  SQL๋ฌธ์œผ๋กœ ๋ณ€๊ฒฝ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๊ณผ์ • ์ค‘๊ฐ„์— SQL๋ฌธ์„ ๊บผ๋‚ด ์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ณด๋„๋ก ํ•˜์ž.

package net.chandol.study.mybatissample.config;

@Slf4j
public class EnhanceMybatisLanguageDriver extends XMLLanguageDriver {
    public EnhanceMybatisLanguageDriver() {
    }

    @Override
    public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
        addDebuggingComment(boundSql);
        return super.createParameterHandler(mappedStatement, parameterObject, boundSql);
    }

    @Override
    public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
        return super.createSqlSource(configuration, script, parameterType);
    }

    @Override
    public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
        return super.createSqlSource(configuration, script, parameterType);
    }

    @SneakyThrows
    private void addDebuggingComment(BoundSql boundSql) {
        Field sqlField = BoundSql.class.getDeclaredField("sql");
        sqlField.setAccessible(true);

        String sql = (String) sqlField.get(boundSql);
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        sql = addParameterComment(sql, parameterMappings);

        sqlField.set(boundSql, sql);
    }

    private String addParameterComment(String sql, List<ParameterMapping> parameters) {
        StringBuilder sqlInternalStringBuilder = new StringBuilder(sql);

        int paramReverseIndex = parameters.size() - 1;
        for (int idx = sql.length() - 1; idx > 0; idx--) {
            char c = sql.charAt(idx);
            if (c == '?') {
                String commentedString = toCommentString(parameters.get(paramReverseIndex).getProperty());

                sqlInternalStringBuilder.insert(idx + 1, commentedString);
                paramReverseIndex = paramReverseIndex - 1;
            }
        }

        return sqlInternalStringBuilder.toString();
    }

    private String toCommentString(String comment) {
        return " /*" + comment + "*/ ";
    }

}

XMLLanguageDriver๋ฅผ ์ƒˆ๋กญ๊ฒŒ ํ™•์žฅํ•˜์—ฌ ์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋กœ์ง์„ ๊ฐ€์ง„ addDebuggingComment ๋ฉ”์„œ๋“œ๋ฅผ ๋งค๋ฒˆ ํ˜ธ์ถœํ•˜๋„๋ก ํ•˜์˜€๋‹ค. ํ•ด๋‹น ๋ฉ”์„œ๋“œ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜์„ ์ด์šฉํ•ด ๋‚ด๋ถ€์˜ SQL๋ฌธ์„ ๋ฝ‘์•„๋‚ด์—ˆ๊ณ , addParameterComment์—์„œ๋Š” "?"์˜ ์œ„์น˜๋ฅผ ์ฐพ์•„๋‚ด์–ด paramter์˜ ID๊ฐ’์„ ๋งคํ•‘ํ•˜๋„๋ก ๋งŒ๋“ค์—ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ƒˆ๋กญ๊ฒŒ ํ™•์žฅํ•œ LanguageDriver๋Š” ์•„๋ž˜์™€ ๊ฐ™์ด application.properties๋ฅผ ํ†ตํ•ด์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

mybatis.configuration.default-scripting-language=net.chandol.study.mybatissample.config.EnhanceMybatisLanguageDriver

์ด์ œ ํ…Œ์ŠคํŠธ๋ฅผ ๋Œ๋ ค๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด "?" ๋’ค์— ์ฝ”๋ฉ˜ํŠธ๊ฐ€ ์ถ”๊ฐ€๋˜์–ด ์–ด๋–ค ํŒŒ๋ผ๋ฏธํ„ฐ์™€ ๋งคํ•‘๋˜์—ˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ด๋‹ค.

2018-07-30 23:17:06.718 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : ==>  Preparing: INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES (? /*name*/ , ? /*country*/ , ? /*population*/ ) 
2018-07-30 23:17:06.738 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : ==> Parameters: ๋‰ด์š•(String), ๋ฏธ๊ตญ(String), 1000000(Long)
2018-07-30 23:17:06.738 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : <==    Updates: 1
2018-07-30 23:17:06.738 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : ==>  Preparing: INSERT INTO CITY (NAME, COUNTRY, POPULATION) VALUES (? /*name*/ , ? /*country*/ , ? /*population*/ ) 
2018-07-30 23:17:06.739 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : ==> Parameters: ๋Ÿฐ๋˜(String), ์˜๊ตญ(String), 2000000(Long)
2018-07-30 23:17:06.739 DEBUG 29739 --- [           main] n.c.s.m.mapper.CityMapper.insertCity     : <==    Updates: 1

๋งบ์Œ

์œ„์˜ ๋™์ž‘ํ•˜๋Š”, ์„ค์ •๋œ ์ฝ”๋“œ๋Š” ์•„๋ž˜ github ์ €์žฅ์†Œ์—์„œ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ๋‹ค.

https://github.com/JAVACAFE-STUDY/mybatis-sample

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages