elevne's Study Note
웹 개발 공부 (MyBatis) 본문
오늘은 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://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
'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 |