๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Spring

Spring Boot + MyBatis ์„ค์ • ๋ฐฉ๋ฒ•(HikariCP, H2)

by Leica 2020. 8. 1.
๋ฐ˜์‘ํ˜•

Spring Boot + MyBatis ์„ค์ • ๋ฐฉ๋ฒ•(HikariCP, H2)

๐Ÿ“ ์ˆœ์„œ

1. ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ
2. ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ ์„ค์ •(schema.sql, data.sql)
3. DBCP/DataSource ์„ค์ •(HikariCP)
4. MyBatis ์„ค์ •(@MapperScan, XML ์œ„์น˜, CamelCase, Alias, ๋กœ๊ทธ๋ ˆ๋ฒจ)
5. Model, Mapper ์ƒ์„ฑ
6. ํ…Œ์ŠคํŠธ

 

1. ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ

MyBatis๋ฅผ ์ด์šฉํ•œ DB ์—ฐ๋™์„ ์œ„ํ•œ ์ƒˆ ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ๋Š” IDE๋ฅผ ์ด์šฉํ•˜๋˜์ง€, spring initializr(start.spring.io)๋ฅผ ์ด์šฉํ•ด ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์ž๋ฐ”๋Š” 8๋กœ, ์˜์กด์„ฑ์€ Spring Web, Spring Data JDBC, Spring Boot DevTools, MyBatis, H2, Lombok์„ ๋„ฃ์–ด์ฃผ์—ˆ๋‹ค.

 

๐Ÿ“ Spring Boot DevTools
์Šคํ”„๋ง ๋ถ€ํŠธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๊ฐœ๋ฐœํ•  ๋•Œ ์—ฌ๋Ÿฌ ํŽธ์˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ.
์—ฌ๊ธฐ์„œ๋Š” H2 ์ฝ˜์†”์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.
H2 ์ฝ˜์†”์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•œ ๋˜ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์€ application.properties์— ๋‹ค์Œ ํ”„๋กœํผํ‹ฐ๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
spring.h2.console.enabled=true (์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์žฌ์‹œ์ž‘ ํ•„์š”)

๐Ÿ“ H2 Database

์œ„์™€ ๊ฐ™์ด ์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ๋•Œ H2 Database ์˜์กด์„ฑ์„ ์ถ”๊ฐ€ํ•˜๋ฉด ๊ฐ„๋‹จํžˆ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ ์ง€์›ํ•˜๋Š” ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋Š” H2, HSQL, Derby๊ฐ€ ์žˆ๋‹ค.
์ด ์ค‘ H2๊ฐ€ ์ž์ฃผ ์‚ฌ์šฉ๋˜๊ณ  ์ถ”์ฒœ๋˜๋Š” ์ด์œ ๋Š” ์ฝ˜์†”์ด ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๐Ÿ“ Spring Data JDBC
Spring JDBC์™€ ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ํด๋ž˜์ŠคํŒจ์Šค์— ์žˆ์œผ๋ฉด(= ์˜์กด์„ฑ ์„ค์ •์ด ๋˜์–ด์žˆ์œผ๋ฉด) ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ DataSource, JdbcTemplate ๋นˆ์„ ์ž๋™์œผ๋กœ ์„ค์ •ํ•ด์ค€๋‹ค.

 

2. ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ ์„ค์ •

/src/main/resources์— schema.sql, data.sql์„ ์ƒ์„ฑํ•˜์—ฌ ๋ณธ ์˜ˆ์ œ์—์„œ ์‚ฌ์šฉํ•  ํ…Œ์ด๋ธ”์„ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ insertํ•˜๋„๋ก ํ•œ๋‹ค.

 

schema.sql

DROP TABLE IF EXISTS Products;

CREATE TABLE Products
(
    prod_id     IDENTITY        PRIMARY KEY,
    prod_name   VARCHAR(255)    NOT NULL,
    prod_price  INT             NOT NULL
);

 

data.sql

INSERT INTO Products (prod_name, prod_price) values ('๋ฒ ๋ฒ ์ˆฒ ๋ฌผํ‹ฐ์Šˆ', 2700);
INSERT INTO Products (prod_name, prod_price) values ('์—ฌ๋ฆ„ ํ† ํผ', 35180);
INSERT INTO Products (prod_name, prod_price) values ('ํŽ˜์ดํฌ ์‚ญ์Šค', 860);
INSERT INTO Products (prod_name, prod_price) values ('์šฐ์‚ฐ', 2900);

 

๐Ÿ“ ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” DDL ์Šคํฌ๋ฆฝํŠธ์™€ DML ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์„ค์ •ํ•˜์—ฌ ์Šคํ‚ค๋งˆ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์ดˆ๊ธฐํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค(DML ์Šคํฌ๋ฆฝํŠธ). ๊ธฐ๋ณธ์ ์œผ๋กœ DDL ์Šคํฌ๋ฆฝํŠธ๋Š” schema.sql, DML ์Šคํฌ๋ฆฝํŠธ๋Š” data.sql์„ ๋กœ๋“œํ•œ๋‹ค.

์ฐธ๊ณ : docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-initialize-a-database-using-spring-jdbc

 

์ฐธ๊ณ  - DataSource์™€ JdbcTemplate ๋นˆ ์ž๋™ ์„ค์ • ํ™•์ธ

์Šคํ”„๋ง ๋ถ€ํŠธ ํ”„๋กœ์ ํŠธ์˜ ์˜์กด์„ฑ์— Spring JDBC์™€ H2์™€ ๊ฐ™์€ ์ธ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด AutoConfiguration์— ์˜ํ•ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๋™์— ํ•„์š”ํ•œ DataSource, JdbcTemplate ๋นˆ์ด ์ž๋™์œผ๋กœ ์ฃผ์ž…๋˜๋ฏ€๋กœ ํŠน์ • datasource๋ฅผ ์„ค์ •ํ•˜์ง€ ์•Š์•„๋„ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ์ฆ‰์‹œ ์—ฐ๋™ํ• ์ˆ˜ ์žˆ๋‹ค.

 

์ด๋ฅผ ์ง์ ‘ ํ™•์ธํ•ด๋ณด๊ธฐ ์œ„ํ•ด ApplicationRunner๋ฅผ ์ƒ์„ฑํ•˜์—ฌ @Autowired๋กœ DataSource ๋นˆ์„ ์ฃผ์ž…๋ฐ›์•„ Connection์˜ ๋ฉ”ํƒ€ ์ •๋ณด๋ฅผ ์ฐ์–ด๋ณด์ž. ์ด ๋ถ€๋ถ„์€ ์ฐธ๊ณ ์šฉ์ด๋ฏ€๋กœ ์Šคํ‚ตํ•ด๋„ ๋ฌด๋ฐฉํ•˜๋‹ค.

 

ApplicationRunner ์ƒ์„ฑ ์œ„์น˜

 

@Slf4j
@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    DataSource dataSource;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        Connection connection = dataSource.getConnection();
        log.info("Url: " + connection.getMetaData().getURL());
        log.info("UserName: " + connection.getMetaData().getUserName());

    }
}

ApplicationRunner์—๋Š” @Component๋ฅผ ๋ถ™์—ฌ์ค˜์•ผ ํ•จ์— ์œ ์˜ํ•˜์ž.

 

๐Ÿ–ฅ ์‹คํ–‰ ๊ฒฐ๊ณผ

...
INFO --- [           main] TestRunner  : Url: jdbc:h2:mem:testdb
INFO --- [           main] TestRunner  : UserName: SA
...

 

๐Ÿ“ ์Šคํ”„๋ง ๋ถ€ํŠธ ์ธ๋ฉ”๋ชจ๋ฆฌ DB ์ด๋ฆ„ ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ 2.3๋ถ€ํ„ฐ ์ธ๋ฉ”๋ชจ๋ฆฌ DB๊ฐ€ ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ์ด๋ฆ„์œผ๋กœ ๋งŒ๋“ค์–ด ์ง€๋„๋ก ๋ณ€๊ฒฝ๋˜์—ˆ๋‹ค. ์ด์ „ ๋ฒ„์ „์—์„œ์™€ ๊ฐ™์ด testdb๋กœ ๊ณ ์ •ํ•˜๋ ค๋ฉด ๋‹ค์Œ ์„ค์ •์„ application.properties์— ์ถ”๊ฐ€ํ•˜๋ฉด ๋œ๋‹ค.

spring.datasource.generate-unique-name=false

 

H2 ์ฝ˜์†”์„ ํ†ตํ•ด DB ์ดˆ๊ธฐํ™” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ œ๋Œ€๋กœ ์ ์šฉ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

 

๋ธŒ๋ผ์šฐ์ €์—์„œ http://localhost:8080/h2-console์— ์ ‘์†ํ•œ๋‹ค.

 

์œ„์™€ ๊ฐ™์ด JDBC URL ์ž…๋ ฅ ํ›„ Connect ํด๋ฆญ

 

 

์ถ”๊ฐ€๋กœ JdbcTemplate๋„ ์ฃผ์ž…๋ฐ›์•„ ์‚ฌ์šฉํ•ด๋ด„์œผ๋กœ์จ ๋นˆ์ด ์ž๋™ ์„ค์ • ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

 

TestRunner.java

@Slf4j
@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    DataSource dataSource;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        // DataSource
        Connection connection = dataSource.getConnection();
        log.info("Url: " + connection.getMetaData().getURL());
        log.info("UserName: " + connection.getMetaData().getUserName());

        // JdbcTemplate
        jdbcTemplate.execute("INSERT INTO Products (prod_name, prod_price) values ('๋ฒ„ํ‚ทํ–‡', 6900)");
    }
}

 

 

3. DBCP/DataSource ์„ค์ •

application.properties - datasource ์„ค์ •

# DataSource
spring.datasource.url=jdbc:h2:mem:mybatis-test
spring.datasource.username=sa
spring.datasource.password=

 

์ฐธ๊ณ ๋กœ spring.datasource.driver-class-name์€ ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ url์„ ๋ณด๊ณ  ์ถ”์ธกํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๋ช…์‹œํ•˜์ง€ ์•Š์•„๋„ ๋œ๋‹ค.

 

์Šคํ”„๋ง ๋ถ€ํŠธ์—์„œ๋Š” HikariCP, Tomcat CP, Commons DBCP2๋ฅผ ์ง€์›ํ•˜๋ฉฐ ์Šคํ”„๋ง ๋ถ€ํŠธ 2์ ๋Œ€์—์„œ๋Š” HikariCP๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์„œ๋„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ  HikariCP๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

ํ˜„์žฌ ํ”„๋กœ์ ํŠธ์—๋Š” HikariCP์— ๋ณ„๋„๋กœ ์„ค์ •ํ•  ๊ฒƒ์€ ์—†์œผ๋ฏ€๋กœ ์„ค์ • ๋ฐฉ๋ฒ•๋งŒ ๊ฐ„๋‹จํžˆ ๋‹ค๋ฃฌ๋‹ค.

 

HikariCP๋Š” application.properties์— spring.datasource.hikari.* ํ”„๋กœํผํ‹ฐ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

์˜ˆ: HikariCP Maximum Pool Size๋ฅผ 4๋กœ ์„ค์ •

spring.datasource.hikari.maximum-pool-size=4

 

์ด์ œ datasource๊ฐ€ ์ •์ƒ์ ์œผ๋กœ ์„ค์ •๋๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

TestRunner.java

@Slf4j
@Component
public class TestRunner implements ApplicationRunner {

    @Autowired
    DataSource dataSource;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        // DataSource
        Connection connection = dataSource.getConnection();
        log.info("DBCP: " + dataSource.getClass()); // ์‚ฌ์šฉํ•˜๋Š” DBCP ํƒ€์ž… ํ™•์ธ
        log.info("Url: " + connection.getMetaData().getURL());
        log.info("UserName: " + connection.getMetaData().getUserName());

        // JdbcTemplate
        jdbcTemplate.execute("INSERT INTO Products (prod_name, prod_price) values ('๋ฒ„ํ‚ทํ–‡', 6900)");
    }
}

log.info("DBCP: " + dataSource.getClass()); ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•˜์˜€๋‹ค.

 

๐Ÿ–ฅ ์‹คํ–‰ ๊ฒฐ๊ณผ

...
INFO [  restartedMain] TestRunner  : DBCP: class com.zaxxer.hikari.HikariDataSource
INFO [  restartedMain] TestRunner  : Url: jdbc:h2:mem:mybatis-test
INFO [  restartedMain] TestRunner  : UserName: SA
...

 

๋กœ๊ทธ๋ฅผ ํ†ตํ•ด ์„ค์ •ํ•œ ๋Œ€๋กœ h2 DB ์ด๋ฆ„์ด mybatis-test๋กœ ๋ฐ”๋€Œ์—ˆ๊ณ , HikariCP๋ฅผ ์‚ฌ์šฉํ•จ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค.

 

4. MyBatis ์„ค์ •

์Šคํ”„๋ง ๋ถ€ํŠธ ๋ฉ”์ธ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜์— @MapperScan์„ ์ด์šฉํ•ด ์Šคํ”„๋ง ๋ถ€ํŠธ๊ฐ€ @Mapper๊ฐ€ ๋ถ™์€ MyBatis ๋งคํผ๋ฅผ ์Šค์บ”ํ•˜์—ฌ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

 

import org.mybatis.spring.annotation.MapperScan;

@MapperScan(basePackageClasses = MybatisSampleApplication.class)
@SpringBootApplication
public class MybatisSampleApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisSampleApplication.class, args);
    }

}

์„ค์ • ๋ฐฉ๋ฒ•์€ @ComponentScan๊ณผ ์œ ์‚ฌํ•˜๋‹ค.

 

application.properties์— MyBatis์— ๊ด€ํ•œ 4๊ฐ€์ง€ ์„ค์ •์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

# MyBatis
# mapper.xml ์œ„์น˜ ์ง€์ •
mybatis.mapper-locations: mybatis-mapper/**/*.xml

# model ํ”„๋กœํผํ‹ฐ camel case ์„ค์ •
mybatis.configuration.map-underscore-to-camel-case=true

# ํŒจํ‚ค์ง€ ๋ช…์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋„๋ก alias ์„ค์ •
mybatis.type-aliases-package=com.atoz_develop.mybatissample.model

# mapper ๋กœ๊ทธ๋ ˆ๋ฒจ ์„ค์ •
logging.level.com.atoz_develop.mybatissample.repository=TRACE

 

5. Model, Mapper(Interface, XML) ์ƒ์„ฑ

(1) Model

model ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ์œ„์—์„œ ๋งŒ๋“  Proudcts ํ…Œ์ด๋ธ”๊ณผ ๋งคํ•‘๋˜๋Š” Model์„ ์ƒ์„ฑํ•œ๋‹ค.

 

 

Product.java

import lombok.*;
import org.apache.ibatis.type.Alias;

@NoArgsConstructor @RequiredArgsConstructor @Getter @Setter @ToString
public class Product {

    private Long prodId;
    @NonNull private String prodName;
    @NonNull private int prodPrice;
}

application.properties์— ์„ค์ •ํ•œ MyBatis ์„ค์ • ์ค‘

mybatis.configuration.map-underscore-to-camel-case=true

์— ์˜ํ•ด ํ”„๋กœํผํ‹ฐ์— ์นด๋ฉœ ์ผ€์ด์Šค ๋„ค์ด๋ฐ ์ปจ๋ฒค์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

@NonNull์€ @RequiredArgsConstructor๋กœ ํ•ด๋‹น ํ•„๋“œ(prodName, prodPrice)๋ฅผ ๋ฐ›๋Š” ์ƒ์„ฑ์ž๋ฅผ ๋งŒ๋“ค๊ธฐ ์œ„ํ•ด ๋ถ™์—ฌ์ฃผ์—ˆ์œผ๋‚˜, @NonNull์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  final๋กœ ์„ ์–ธํ•ด์ค˜๋„ ๊ฐ€๋Šฅํ•˜๋‹ค.

private final String prodName;
private final int prodPrice;

 

(2) Mapper Interface

repository ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ๋งคํผ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ Prouducts ํ…Œ์ด๋ธ”๊ณผ ์—ฐ๋™ํ•  ๋ฉ”์†Œ๋“œ๋ฅผ ์ •์˜ํ•œ๋‹ค.

 

ProuductMapper.java

@Mapper
public interface ProductMapper {

    Product selectProductById(Long id);
    List<Product> selectAllProducts();
    void insertProduct(Product product);
}

@MapperScan์— ์˜ํ•ด Mapper๋กœ ์Šค์บ”๋  ์ˆ˜ ์žˆ๋„๋ก @Mapper ์• ๋…ธํ…Œ์ด์…˜์„ ๋ถ™์—ฌ์ค€๋‹ค.

 

(3) XML

์œ„์—์„œ application.properties์— MyBatis XML ์œ„์น˜๋ฅผ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ–ˆ์—ˆ๋‹ค.

 

# mapper.xml ์œ„์น˜ ์ง€์ •
mybatis.mapper-locations: mybatis-mapper/**/*.xml

 

ํ•ด๋‹น ์œ„์น˜์— XML ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜์—ฌ ProductMapper ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ์‹œ ์‹ค์ œ๋กœ ๋™์ž‘ํ•  SQL์„ ์„ค์ •ํ•œ๋‹ค.

 

 

ProudctMapper.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">

<mapper namespace="com.atoz_develop.mybatissample.repository.ProductMapper">

    <select id="selectProductById" resultType="Product">
        SELECT prod_id
              ,prod_name
              ,prod_price
        FROM products
        WHERE prod_id = #{prodId}
    </select>

    <select id="selectAllProducts" resultType="Product">
        SELECT prod_id
              ,prod_name
              ,prod_price
        FROM products
    </select>

    <insert id="insertProduct" parameterType="Product">
      INSERT INTO products (prod_name, prod_price)
      VALUES (#{prodName}, #{prodPrice})
    </insert>

</mapper>

application.properties์— ์„ค์ •ํ•œ MyBatis ์„ค์ • ์ค‘

mybatis.type-aliases-package=com.atoz_develop.mybatissample.model

์— ์˜ํ•ด type ๋งคํ•‘ ์‹œ ํŒจํ‚ค์ง€ ๋ช…์„ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ๋‹ค.

 

6. ํ…Œ์ŠคํŠธ

Service ๋นˆ๊ณผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์‹ค์ œ๋กœ ํ…Œ์ŠคํŠธํ•ด๋ณด์ž.

 

service ํŒจํ‚ค์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ProudctService๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

ProductService.java

@Service
public class ProductService {

    @Autowired
    private ProductMapper productMapper;

    public Product getProductById(Long id) {

        return productMapper.selectProductById(id);
    }

    public List<Product> getAllProducts() {

        return productMapper.selectAllProducts();
    }

    @Transactional
    public void addProduct(Product product) {

        productMapper.insertProduct(product);
    }
}

 

ProductMapper๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ๊ฐ ์„œ๋น„์Šค ๋ฉ”์†Œ๋“œ์—์„œ ํ˜ธ์ถœํ•œ๋‹ค.

 

๋‹ค์Œ์œผ๋กœ ProductService์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.

 

ProuductServiceTest.java

@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class ProductServiceTest {

    @Autowired
    private ProductService productService;

    @Test
    public void getProductById() {
        Product product = productService.getProductById(1L);
        log.info("product : {}", product);
    }

    @Test
    public void getAllProducts() {
        List<Product> products = productService.getAllProducts();
        log.info("products : {}", products);
    }

    @Transactional
    @Test
    public void addProduct() {
        productService.addProduct(new Product("์ฟค๋‹ฌ ์ƒดํ‘ธ", 7900));
        productService.addProduct(new Product("๋งˆ์ŠคํฌํŒฉ", 1000));
        productService.addProduct(new Product("ํ‹ฐ์…”์ธ ", 5900));
    }
}

 

์ด์ œ MyBatis ์„ค์ •๊ณผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ์™„๋ฃŒ๋˜์—ˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๐Ÿ–ฅ ์‹คํ–‰ ๊ฒฐ๊ณผ

getProductById

DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectProductById  : ==>  Preparing: SELECT prod_id ,prod_name ,prod_price FROM products WHERE prod_id = ?
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectProductById  : ==> Parameters: 1(Long)
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectProductById  : <==    Columns: PROD_ID, PROD_NAME, PROD_PRICE
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectProductById  : <==        Row: 1, ๋ฒ ๋ฒ ์ˆฒ ๋ฌผํ‹ฐ์Šˆ, 2700
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectProductById  : <==      Total: 1
 INFO 956 --- [           main] c.a.m.service.ProductServiceTest         : product : Product(prodId=1, prodName=๋ฒ ๋ฒ ์ˆฒ ๋ฌผํ‹ฐ์Šˆ, prodPrice=2700)

 

getAllProducts

DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : ==>  Preparing: SELECT prod_id ,prod_name ,prod_price FROM products
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : ==> Parameters: 
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==    Columns: PROD_ID, PROD_NAME, PROD_PRICE
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==        Row: 1, ๋ฒ ๋ฒ ์ˆฒ ๋ฌผํ‹ฐ์Šˆ, 2700
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==        Row: 2, ์—ฌ๋ฆ„ ํ† ํผ, 35180
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==        Row: 3, ํŽ˜์ดํฌ ์‚ญ์Šค, 860
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==        Row: 4, ์šฐ์‚ฐ, 2900
TRACE 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==        Row: 5, ๋ฒ„ํ‚ทํ–‡, 6900
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.selectAllProducts  : <==      Total: 5
 INFO 956 --- [           main] c.a.m.service.ProductServiceTest         : products : [Product(prodId=1, prodName=๋ฒ ๋ฒ ์ˆฒ ๋ฌผํ‹ฐ์Šˆ, prodPrice=2700), Product(prodId=2, prodName=์—ฌ๋ฆ„ ํ† ํผ, prodPrice=35180), Product(prodId=3, prodName=ํŽ˜์ดํฌ ์‚ญ์Šค, prodPrice=860), Product(prodId=4, prodName=์šฐ์‚ฐ, prodPrice=2900), Product(prodId=5, prodName=๋ฒ„ํ‚ทํ–‡, prodPrice=6900)]

 

addProudct

 INFO 956 --- [           main] o.s.t.c.transaction.TransactionContext   : Began transaction (1) for test context [DefaultTestContext@327af41b testClass = ProductServiceTest, testInstance = com.atoz_develop.mybatissample.service.ProductServiceTest@7740b0ab, testMethod = addProduct@ProductServiceTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6cb6decd testClass = ProductServiceTest, locations = '{}', classes = '{class com.atoz_develop.mybatissample.MybatisSampleApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@40005471, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@49438269, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@7770f470, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@71e9ddb4, org.springframework.boot.test.context.SpringBootTestArgs@1], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]; transaction manager [org.springframework.jdbc.datasource.DataSourceTransactionManager@3eb9c575]; rollback [true]
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==>  Preparing: INSERT INTO products (prod_name, prod_price) VALUES (?, ?)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==> Parameters: ์ฟค๋‹ฌ ์ƒดํ‘ธ(String), 7900(Integer)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : <==    Updates: 1
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==>  Preparing: INSERT INTO products (prod_name, prod_price) VALUES (?, ?)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==> Parameters: ๋งˆ์ŠคํฌํŒฉ(String), 1000(Integer)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : <==    Updates: 1
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==>  Preparing: INSERT INTO products (prod_name, prod_price) VALUES (?, ?)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : ==> Parameters: ํ‹ฐ์…”์ธ (String), 5900(Integer)
DEBUG 956 --- [           main] c.a.m.r.ProductMapper.insertProduct      : <==    Updates: 1
 INFO 956 --- [           main] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: [DefaultTestContext@327af41b testClass = ProductServiceTest, testInstance = com.atoz_develop.mybatissample.service.ProductServiceTest@7740b0ab, testMethod = addProduct@ProductServiceTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@6cb6decd testClass = ProductServiceTest, locations = '{}', classes = '{class com.atoz_develop.mybatissample.MybatisSampleApplication}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true}', contextCustomizers = set[org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@40005471, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@49438269, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@7770f470, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@71e9ddb4, org.springframework.boot.test.context.SpringBootTestArgs@1], resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.boot.test.context.SpringBootContextLoader', parent = [null]], attributes = map['org.springframework.test.context.web.ServletTestExecutionListener.activateListener' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.populatedRequestContextHolder' -> true, 'org.springframework.test.context.web.ServletTestExecutionListener.resetRequestContextHolder' -> true]]

 

References

mybatis.org/spring-boot-starter/mybatis-spring-boot-autoconfigure/#

mybatis.org/mybatis-3/ko/getting-started.html

tech.javacafe.io/2018/07/31/mybatis-with-spring/

taetaetae.github.io/2019/04/21/spring-boot-mybatis-mysql-xml/

goddaehee.tistory.com/205

linked2ev.github.io/gitlog/2019/08/21/springboot-mvc-4-%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-MyBatis-+-HikariCP-+-MariaDB-%EC%84%A4%EC%A0%95/

lts0606.tistory.com/249

๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€