elevne's Study Note

웹 개발 공부 (MyBatis) 본문

Backend/Spring

웹 개발 공부 (MyBatis)

elevne 2022. 11. 16. 20:45

오늘은 Spring framework에서 MyBatis를 사용하는 법에 대해서 다시 한 번 정리해보았다. 우선 Spring Initializr에서 다음과 같은 dependency 들을 추가해준다.

 

 

 

의존성

 

 

 

build.gradle 파일에서 의존성 관련 부분을 확인해보면 아래와 같이 적혀있을 것이다.

 

 

 

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'mysql:mysql-connector-java'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

 

 

 

 

의존성 주입을 해준 이후, application.properties 파일에서 몇 가지 설정을 더 해주어야 한다. 설정해줘야 하는 것들은 MySQL 주소, 포트, 스키마 이름, MySQL Username, password 등이 있다. 나는 아래와 같이 적어주었다.

 

 

 

server.port=8090

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=(UserName적기)
spring.datasource.password=(비밀번호적기)
spring.datasource.jdbc-url=jdbc:mysql://localhost:3306/mysqltest?serverTimezone=UTC&characterEncoding=UTF-8
spring.datasource.url=jdbc:mysql://localhost:3306/mysqltest
spring.datasource.mapper-locations=classpath:/mapper/**/*.xml

 

 

 

그렇게 해준 다음, Elasticsearch를 Spring에서 사용했을 때와 마찬가지로 우선 Config 파일을 작성해줘야 한다. config 디렉토리를 만들고 아래와 같이 MyBatisConfig 파일을 작성해주었다.

 

 

 

package com.example.bike.config;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;

@Configuration
@MapperScan(value = "com.example.bike", sqlSessionFactoryRef = "SqlSessionFactory")
public class MyBatisConfig {

    @Value("${spring.datasource.mapper-locations}")
    String mPath;

    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource DataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory SqlSessionFactory(@Qualifier("dataSource") DataSource DataSource, ApplicationContext applicationContext) throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(DataSource);
        sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources(mPath));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean(name = "SessionTemplate")
    public SqlSessionTemplate SqlSessionTemplate(@Qualifier("SqlSessionFactory") SqlSessionFactory firstSqlSessionFactory) {
        return new SqlSessionTemplate(firstSqlSessionFactory);
    }
}

 

 

 

@MapperScan을 사용하여 해당하는 패키지 경로 밑에 있는 Interface들은 전부 매퍼로 사용할 수 있게 된다. 또, DataSource에서 사용한 @ConfigurationProperties는 properties 파일의 key 값이 해당하는 prefix의 값으로 시작할 때, 그 값들을 묶어서 Bean으로 등록할 수 있게끔 해준다. 위의 경우에는 properties가 애플리케이션 안에 있지 않고 jar 파일 안에 있는 경우인 것으로 보인다. 이 경우에는 위처럼 @Bean 어노테이션을 사용해준 후 @ConfigurationProperties를 사용해주면 된다. 이렇게 만든 Bean 객체를 제대로, 착오 없이 가져오기 위해 @Qualifier(bean이름) 을 사용할 수 있다.

 

 

 

또, MyBatis에서 사용되는 것이 DataSource와 SqlSessionFactory가 있다. SqlSessionFactory는 DB와의 연결과 SQL을 실행시켜주는 중요한 객체인데 이는 JDBC DataSource의 필수 프로퍼티가 필요하다. DataSource를 위와 같이 만들어주고 이를 SqlSessionFactoryBean 안에 넣어준다. 그 후, ApplicationContext.getResources를 사용하여 Mapper Location을 추가해줄 수 있다.(Application Context는 Bean Factory를 상속받아 확장한 것으로, getResource 함수를 사용하여 Resource 객체를 내부적으로 생성해서 사용할 수 있다.) 여기에서 getObject() 함수를 통해 얻은 SqlSessionFactory 객체를 다시 한 번 밑에서 SqlSessionTemplate 안에 넣어주면 되겠다.

 

 

위와 같이 Config 설정을 마무리하고, 필요한 DTO Class를 만들어주었다. (이전에 작성해둔 코드가 예전에 진행했던 Bike project에 사용한 것이라 Class 이름이 Travel로 되어있다.)

 

 

 

package com.example.bike.entity;

import lombok.Data;

@Data
public class Travel {
    private int id;

    private float latitude;

    private float longitude;

    private String nearest;

    private int rank;

    private String category;

    private float time;

    private String name;
}

 

 

DTO를 만들어준 이후, 실질적으로 MyBatis의 기능을 사용할 Interface를 아래와 같이 작성해주었다.

 

 

 

package com.example.bike.mapper;

import com.example.bike.entity.Travel;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface TravelMapper {

    List<Travel> findAll();

}

 

 

 

위에서 작성한 함수의 이름 findAll을 id로 하여 Mapper.xml을 작성해줄 것이다. 또, 여기에서는 따로 인자를 받지 않고 쿼리를 실행하도록 하였는데, 인자를 받고 실행하기 위해서는 아래와 같이 작성해줄 수 있다.

 

 

 

List<Travel> findSome(Travel travel);

 

 

 

Interface 안에 작성한 함수가 실질적으로 작동될 수 있도록 쿼리를 추가해주어야 한다. resources/mapper/ 경로에 Mapper.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.example.bike.mapper.TravelMapper">

    <select id="findAll" resultType="com.example.bike.entity.Travel">
        SELECT * FROM TRAVEL ORDER BY rank
    </select>

</mapper>

 

 

 

이제 이 쿼리를 활용할 준비가 다 되었다. Service, Controller 단에서 Interface에서 작성한 함수를 사용하여 코드를 작성해준다.

 

 

 

package com.example.bike.service;

import com.example.bike.entity.Travel;
import com.example.bike.mapper.TravelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class TravelService {

    @Autowired
    TravelMapper travelMapper;

    public List<Travel> findAll() {
        return travelMapper.findAll();
    }
}

 

 

 

package com.example.bike.controller;

import com.example.bike.entity.Travel;
import com.example.bike.service.TravelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

@Controller
public class TravelController {

    @Autowired
    TravelService travelService;

    @GetMapping("/")
    public String root() {
        return "index";
    }

    @GetMapping("/findAll")
    public String findAll(ModelMap model) {
        List<Travel> list = travelService.findAll();
        model.addAttribute("list", list);
        return "test";
    }
}

 

 

 

위와 같이 순서대로 Service, Controller 파일을 작성해주고 /findAll 경로로 들어가면 쿼리의 결과를 확인할 수 있을 것이다. 

 

 

또, MyBatis 활용 방법을 몇 가지 정리해두었다.

 

 

 

 <select id="test" resultType="com.eg.eg.Travel" parameterType="string">

 

 

 

위와 같이 tag 안에 resultType, parameterType을 지정해서 넣어줄 수 있다. (tag는 기본적으로 사용되는 것으로는 Select, Insert, Update, Delete 가 있겠다.)

 

 

 

또 쿼리 내에서 parameterType으로 받은 Class의 값을 사용해야 할 때, 사용해야 하는 값의 이름을 다음과 같이 적어주면 된다.

 

 

 

#{searchTerm}

 

 

 

위와 같이 작성해주면 parameterType으로 넣어준 Class의 searchTerm 값이 ' ' 안에 들어가서 쿼리 내에서 사용된다. 만약 소괄호 안에 안넣고 값 그대로 사용하고 싶다면 # 대신에 $ 를 중괄호 앞에 붙여주면 된다.

 

 

 

또, if 문을 사용할 수 있는데 사용법은 아래와 같이 test="~~~" 를 적어주면 된다. test 란에 적어준 경우에 해당하는 경우 태그 안의 쿼리가 포함되어 실행되는 것이다.

 

 

<if test="searchTerm != null and searchTerm != ''">
추가할 쿼리
</if>

 

 

 

if문 처럼 또 사용할 수 있는 것이 choose 문인데 사용법은 아래와 같다. 직관적이어서 보면 바로 이해할 수 있다.

 

 

 

<choose>
    <when test="orderColumn == '' or orderColumn == null">
     TITLE 
    </when>
    <otherwise>
     ${orderColumn}
    </otherwise>
</choose>

 

 

 

또, MyBatis 쿼리 내에서 for 문도 사용할 수 있다. 쿼리 안에서 IN을 사용하는 법에 대해서 알아야 하는데 이 또한 간단하다. 위의 쿼리 대신에 아래 쿼리 처럼 적어줄 수 있는 것이다. 사용법은 아래와 같다.

 

 

 

SELECT * FROM TEST WHERE TITLE="1" OR TITLE="2";

SELECT * FROM TEST WHERE TITLE IN ("1", "2");

 

 

 

그래서 저 IN 뒤에 오는 괄호 안에 for 문으로 값을 채워줄 수 있는 것인데 아래처럼 적어주면 되겠다.

 

 

 

SELECT * FROM TEST WHERE TITLE IN
<foreach collection="list" index="index" item="obj" open="(" close=")" separator=",">
#{obj}
</foreach>

 

 

 

인자들을 하나씩 살펴보자면 Collection은 전달받은 인자값, index는 목록의 위치 값, item은 전달받은 인자값을 다름 이름으로 지정해줄 수 있는 것, open, close는 해당 구문이 시작/종료될 때 넣을 문자, separator은 반복 사이마다 넣어줄 값을 뜻한다.

 

 

 

또, MyBatis 내에서 쿼리를 작성할 때 < 부등호를 사용하면 태그가 열리는 것으로 인식하여 오류가 난다. 이를 위해서는 아래와 같이 작성해줘야 한다.

 

 

 

<![CDATA[<]]>

 

 

 

 

출처:

https://sunghs.tistory.com/77

https://kookyungmin.github.io/server/2018/08/13/spring_06/

https://atoz-develop.tistory.com/entry/Spring-Resource-%EC%B6%94%EC%83%81%ED%99%94

https://m.blog.naver.com/10hsb04/221708690010

'Backend > Spring' 카테고리의 다른 글

Spring Boot 복습 (5)  (0) 2023.04.09
Spring Boot 복습 (4)  (0) 2023.04.08
Spring Boot 복습 (3)  (0) 2023.04.07
Spring Boot 복습 (2)  (0) 2023.04.05
Spring Boot 복습 (1)  (0) 2023.04.04