이번에 해볼것은 MySQL을 Spring Boot에 연동하는것이다.
일단 MySQL이 뭐냐면 DBMS중 하나로써 데이터를 관리하는 프로그램중에 하나이다.
우선적으로 MySQL을 사용하려면 MySQL이 설치되어 있어야 한다.
설치 방법은 아래 링크를 보고 설명대로 설치를 해주면 된다.
https://goddaehee.tistory.com/277
설치를 완료 했다면 이제 해야 할일은 설치한 MySQL과 Spring Boot를 연동하는것이다.
MySQL과 Spring Boot를 연동할때 우리는 MyBatis를 사용할것이다.
사용에 앞서서 MyBatis가 뭔지부터 알아보자.
MyBatis란?
Mybatis는 자바 오브젝트와 SQL사이의 자동 매핑 기능을 지원하는 ORM(Object relational Mapping)프레임워크이다.
SQL을 별도의 파일로 분리해서 관리하게 해준다.
MyBatis 특징
- 복잡한 쿼리나 다이나믹한 쿼리에 강하다.
- 프로그램 코드와 SQL 쿼리의 분리로 코드의 간결성 및 유지보수성 향상
- resultType, resultClass등 Vo를 사용하지 않고 조회결과를 사용자 정의 DTO, MAP 등으로 맵핑하여 사용 할 수 있다.
- 빠른 개발이 가능하여 생산성이 향상된다.
그럼 이제 MyBatis가 뭔지 알았으니 이제 MyBatis를 이용해서 MySQL과 연동을 해보자.
연동을 위해서는 아래와 같은 과정을 거친다.
1. dependency 추가.
2. application.properties에 MySQL 정보 설정하기.
3. 값을 받아올 DTO 생성하기.
4. application.properties에 MyBatis 설정하기.
5. 데이터베이스를 연동 시킬 DAO interface 만들기
6. mapper xml 만들기
7. DAO를 사용할 Service class 만들기
8. Service Class를 사용할 Controller 만들기
그럼 이제 순서대로 자세히 살펴보자.
1. dependency 추가.
우리가 사용할 dependency는 아래와 같다.
mybatis, mysql, ibatis
위 3개의 dependency를 아래의 사이트에서 검색하여 사용 빈도가 제일 높은것 혹은 가장 최신 버전 선택해서
값을 넣는다.
이렇게 3개를 복사해서 pom.xml에 붙여 넣는다.
그럼 이제 mysql connector를 제외하고는 이상이 없을것이다.
mysql connector에서 보면 주황색 줄 같은게 쳐져 있는데 이건 버전 관련된 문제라 그냥 버전만 지워주면 해결이 된다.
따라서 아래와 같은 dependency를 넣어주면 된다.
<!-- mysql과 연동하기 위해서 사용하는 라이브러리-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- mybatis를 사용하기 위해서 사용하는 라이브러리 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- mapper를 사용하기 위해 -->
<dependency>
<groupId>org.apache.ibatis</groupId>
<artifactId>ibatis-core</artifactId>
<version>3.0</version>
</dependency>
2. application.properties에 MySQL 정보 설정하기
이건 이제 mysql과 연동을 할때 접근을 어디로 할것인지와 접근할 아이디와 비밀번호를 적는 부분이다.
이것을 적어야지만 mysql과 연동이 가능하다.
#mysql configuration
spring.datasource.url=jdbc:mysql://localhost:3306/데이터베이스명?serverTimezone=UTC&allowPublicKeyRetrieval=true
spring.datasource.username=유저명
spring.datasource.password=비밀번호
3. 값을 받아올 DTO 생성하기.
그럼 이제 MySQL에서 값을 받아올 Class를 만들어보자.
나는 지역정보에 대한 값을 받아올 것이기 때문에 아래와 같이 만들었다.
파일경로 : com.wook.model.dto
Class 명 : GeoInfo
package com.wook.model.dto;
public class GeoInfo {
private int geoNum;
private int x;
private int y;
public GeoInfo() {
super();
}
public GeoInfo(int geoNum, int x, int y) {
super();
this.geoNum = geoNum;
this.x = x;
this.y = y;
}
public int getGeoNum() {
return geoNum;
}
public void setGeoNum(int geoNum) {
this.geoNum = geoNum;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
@Override
public String toString() {
return "GeoInfo [geoNum=" + geoNum + ", x=" + x + ", y=" + y + "]";
}
}
이제 DTO를 만들었으니 application.properties에 mybatis 설정을 정해주자.
4. application.properties에 MyBatis 설정하기.
우리가 여기서 해야할것은
첫째 SQL의 결과값을 담을 DTO 클래스 파일이 어디에 위치해 있는지 알려주기
둘째 SQL과 연결 지은 쿼리문을 담은 mapper 파일의 위치가 어디에 위치해 있는지 알려주기.
셋째 camelcase 적용할것인지
일단 첫째는 3번 과정에서 만든 DTO의 파일 경로를 적어주면 된다.
mybatis.type-aliases-package=com.wook.model.dto
둘째는 이제 mapper 파일의 위치를 알려주면 되는데 보통 mapper 파일의 위치는
src/main/resources 안에 위치한다.
따라서 mapper xml 파일이 위치할 경로를 입력해주면 해당 경로로 접근하여서 SQL 쿼리문을 실행하게 된다.
셋째는 Mybatis에 CamelCase를 적용시키는것이다.
그럼 이제 MyBatis의 설정을 완료 했으니
mapper xml 파일과 연동할 DAO를 만들어 놓자.
5. 데이터베이스를 연동 시킬 DAO interface 만들기
DAO는 우리는 통상 interface로 만든다.
interface로 만드는 이유는 DAO, Service, Controller 이렇게 역할을 나눠서 분리시킬수 있기 때문이다.
따라서 DAO를 interface로 만든다.
그럼 내가 어떻게 DAO를 만들었는지 일단 먼저 소스를 확인해보자.
package com.wook.model.dao;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.wook.model.dto.GeoInfo;
@Mapper
public interface GeoDao {
List<GeoInfo> getGeoInfo();
}
위 DAO는 Mapper와 연동을 할것이기때문에 @Mapper 어노테이션을 사용하였다.
해당 DAO에서 이제 getGeoInfo()를 호출하면 mapper의 해당 함수와 관련된 쿼리문을 사용해서 결과값을 받아올것이다.
그럼 이제 mapper xml 파일을 만들어보자.
6. mapper xml 만들기
일단은 소스부터 먼저 살펴보자.
나의 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.wook.model.dao.GeoDao">
<select id="getGeoInfo" resultType="GeoInfo">
select * from geo_xy
</select>
</mapper>
일단 mapper의 namespace에는 해당 mapper와 연관 지을 DAO의 위치를 적어준다.
그래야지만 DAO와 연동하여서 SQL문을 실행할수가 있다.
그리고 그 아래에는 사용할 쿼리의 종류에 따라 달라질수 있는데 나같은 경우는 조회를 할것이기 때문에
<select> 를 사용했다.
그리고 resultType에는 GeoInfo만 적어주었다.
GeoInfo만 적을수 있었던 이유는 우리가 4번 과정에서 resultType이 들어가 있는 파일경로들을 적어 주었기때문에
마지막에 필요한 GeoInfo만 적을수 있다.
굳이 저 방식을 사용하고 싶지 않다면 resultType에 결과 클래스의 파일 경로를 모두 적어주면 된다.
ex) resultType="com.wook.model.dto.GeoInfo"
그럼 이제 DAO와 연동을 했고 id 즉 getGeoInfo()라는 함수가 호출이 되었을때 해당 mapper.xml에서
위 명령을 실행하게 되는것이다.
그럼 이제 해당 DAO를 사용할 Service를 만들어보자.
7. DAO를 사용할 Service class 만들기
DAO를 사용할 Service Class 만드는것은 되게 간단하다 그냥 우리가 등록해놓은
Mapper bean을 주입 받아서 사용하면 된다.
소스는 아래와 같다.
package com.wook.service;
import java.util.List;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.stereotype.Service;
import com.wook.model.dao.GeoDao;
import com.wook.model.dto.GeoInfo;
@Service
@MapperScan("com.wook.model.dao") //mapper 스캔하기
public class GeoService {
private GeoDao geodao;
public GeoService(GeoDao geodao) {
this.geodao = geodao; //생성자 주입 받기
}
public List<GeoInfo> getGeoXY() {
return geodao.getGeoInfo();
}
}
일단 제일 중요한것은 @MapperScan 이라는 어노테이션을 사용하여서 등록한 Mapper들을 스캔할수 있게 Mapper들이 등록 되어 있는 패키지를 scan 할수 있게 해야한다.
해당 작업을 하지 않게 되면 우리가 아무리 @Autowired 받아도 빈이 생성되어 있지 않기 때문에 볼수가 없다.
그리고 여기서 DAO는 분명 interface로 만들었는데 어떻게 implements 한 객체가 없음에도 불구하고 해당 interface를 쓸수가 있나 라는 의문이 들수가 있다. (내가 그랬음)
그래서 알아봤는데 bean을 만드는 과정에서 BeanFactoryPostProcessor라는 인터페이스가 개입한다.
여기서 "어떤 인터페이스가 @Mapper 어노테이션으로 지정되어 있다면 스프링 빈으로 등록하라" 라고 명령할수 있다.
BeanFactoryPostProcessor 인터페이스는 Functional Interface로, postProcessBeanFactory(ConfiguratbleListableBeanFactory) 메소드 하나를 선언하고 있다.
따라서 이 인터페이스에 대한 구현체(클래스)를 이용하여 프로그램적으로 새로운 빈을 등록할 수 있다.
더 자세한 내용은 아래 링크에 나와 있으니 참조 바란다.
http://wiki.x2bee.com/pages/viewpage.action?pageId=7767258
그럼 이제 해당 Service를 사용할 Controller를 만들어보자.
8. Service Class를 사용할 Controller 만들기
사실 Controller라고 해봤자 나는 진짜 별거 없다. 왜냐하면 따로 RequestMapping을 해주지 않기 때문이다.
따라서 나의 소스는 아래와 같다.
package com.wook.controller;
import java.time.Duration;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import com.wook.model.dto.ApiKey;
import com.wook.model.dto.GeoInfo;
import com.wook.model.dto.Item;
import com.wook.model.dto.Items;
import com.wook.model.dto.SweatherRootRes;
import com.wook.model.dto.Temperature;
import com.wook.service.GeoService;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
@SpringBootApplication(scanBasePackages= {"com.wook.model","com.wook.weather","com.wook.service"})
public class Application implements CommandLineRunner{
private GeoService gs; //To get GeoService
private Logger logger = LoggerFactory.getLogger(Application.class); //로그를 찍기 위해서 사용하는 Class
@Autowired
public Application( GeoService gs) {
this.gs = gs;
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Override
public void run( String... args ) throws Exception {
for(GeoInfo gi : gs.getGeoXY()) {
logger.info(gi.toString());
}
}
}
이제 앞으로 할것은 DB로 받아온 정보들로 API를 호출해보는것이다.
'코딩일기 > 날씨앱 만들기 프로젝트' 카테고리의 다른 글
[Java] Thread Pool 설명 및 사용 방법 (0) | 2021.11.21 |
---|---|
[Java] Thread 설명 및 사용법 (0) | 2021.11.16 |
[Spring Boot] properties 를 통해서 키 값 숨기기 (0) | 2021.10.24 |
[Spring Boot] WebClient 파라미터 인코딩 하는법 (0) | 2021.10.24 |
[Spring Boot] WebClient 이용한 API 호출 (0) | 2021.10.24 |