Spring Boot & Mybatis 연동해보기!
오늘 수업때 배운 내용은 스프링부트에서 마이바티스를 연동하여 DB에 데이터를 CRUD하는거에 대해서 다뤘습니다!
까먹지 않게 제가 직접 한번 더 구현을 해보고 기록하고자 합니다 🙂
사용할 DB는 MySQL입니다 !
사전준비 : 데이터베이스 모델링 및 구축
테스트이기 때문에…ㅈ..조촐하게 ^^
1. 프로젝트 생성시 Setting
기본적으로 필요한 디펜던시를 설정해줍니다.
- Lombok (선택사항)
- Spring Boot DevTools
- Spring Web
- Thymeleaf (선택사항)
- MySQL Driver (DB연동)
- Mybatis Framework
2. application.properties 설정
server.port=80
# MySQL Driver
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/shopdb?serverTimezone=Asia/Seoul
spring.datasource.username=wooj
spring.datasource.password=245812
# Mybatis 정보 setting
mybatis.type-aliases-package=com.mybatis.dto
mybatis.mapper-locations=com/mybatis/batisxml/*.xml
server.servlet.context-path=/
서버 포트를 설정 해주시고, MySQL Driver와 연결할 기본 정보를 입력해줍니다!
DB에서 사용할 계정의 정보를 기입하고, Mybatis에서 변수들을 연결할 package 경로를 입력해줍니다.
또한 매퍼.xml 파일의 위치와 파일의 형식을 설정해줍니다.
3. Frame이 될 Mapper, Service 인터페이스 클래스 생성
“왜 굳이 인터페이스를 만들어서 상속을 받나요??? 스프링 레거시 프로젝트로 연동했을때는 그냥 각각 DTO나 VO에 따라서 Mapper클래스를 만들고 필요한 메소드를 적었잖아요”
그러게요? 왜 굳이 인터페이스를 만들까요?
자! 각각의 매퍼 클래스에는 공통적으로 사용되는 CRUD라는게 존재합니다! 여기서 말하는 CRUD란
C : CREATE 생성 ⇒ Create
R : READ 읽기 ⇒ Select
U : UPDATE 갱신 ⇒ Update
D : DELETE 삭제 ⇒ Delete
라고 생각하면 됩니다! DB에서 가장 기본적인 데이터 처리기능들을 줄여서 CRUD라고 부르는겁니다.
자 그러면 모든 테이블을 사용할때 가장 기본적으로 들어가는 CRUD라는게 있다고 했는데, 그러면
모든 Mapper 클래스에는 CRUD가 포함되 있는게 혹시 모를 코드의 중복성을 제거하는데 큰 도움이 되겠죠?
이 CRUD의 코드 내용은 회사, 프로젝트, 담당로직 에 따라서 상이합니다.
그러면 인터페이스로 만들어서 메소드만 정의를 하고 나머지 구현은 매퍼들이 상속을 받아서 구현하면 될겁니다.
이때 주의해야 될것은, 바로 매개변수값입니다!
왜냐면 각각의 DTO나 VO클래스는 다르기 때문이죠! 그렇기에 “제너릭” 이라는 자바의 특징을 활용하여야 합니다.
제너릭 타입은 쉽게 말하면 “뭐가 값으로 들어올지 모르니 너가 넣는거에 따라서 값이 변하는 타입이 제너릭이야!”
라고 말을 할 수 있을거 같아요!
제너릭 : 클래스 내부에서 지정하는 것이 아닌 외부에서 사용자에 의해 지정되는 것을 의미
자 그렇다면 기본적인 원리는 이 정도면 다 이해가 됬을거 같으니까 코드를 살펴 봅시다!
Mapper
package com.mybatis.frame;
import java.util.List;
public interface FrameMapper<K,V> {
public void insert(V v) throws Exception;
public void delete(K k) throws Exception;
public void update(V v) throws Exception;
public List<V> selectAll() throws Exception;
public V select(K k) throws Exception;
}
두 개 클래스의 메소드명이 다른걸 알 수 있는데….당연히 헷갈리지 않기 위해서 구별을 지은거죠??…
Service
package com.mybatis.frame;
import java.util.List;
public interface FrameService<K,V> {
public void register(V v) throws Exception;
public void remove(K k) throws Exception;
public void modify(V v) throws Exception;
public List<V> get() throws Exception;
public V get(K k) throws Exception;
}
4. DTO 클래스 생성
package com.mybatis.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class AccountDTO {
private String userid;
private String userpwd;
private String username;
private String usertel;
private String useremail;
}
application.properties 에서 설정한 mybatis.type-aliases-package= 의 위치로 DTO패키지와 그 하위에 클래스를 생성하여 선언해줍니다.
@어노테이션을 사용하여 생성자와 getter, setter , toString을 자동으로 생성해줍니다.
여기서 선언한 변수명은 DB와의 컬럼명과 똑같아야 합니다.
(그래야지 mybatis가 말귀를 쳐 알아들어요;)
5. Mapper 패키지 및 클래스 생성
이제 인터페이스로 구현해놓은 Mapper를 사용하기 위하여 상속을 받을 클래스를 생성해야합니다!
근데 주의할점….여기서 Mapper 클래스가 아닌 인터페이스로 생성해야됩니다!!!!!!!
왜냐구요? 저희는 위에 만들어놓은 FrameMapper의 CRUD를 상속 받음과 동시에 AccountDTO를 활용하여
추가적으로 필요한 메소드를 따로! 정의하기 위해서에요! 는 사실 인터페이스로 하는 주된 목적은 아니고요….
mybatis가 그렇게 하래요 썅 @Mapper가 인터페이스에서만 사용이 가능하거든요….
인터페이스를 인터페이스가 상속을 받게되면 부모에 있는 내용을 가져다 쓸수 있죠? 그와 별개로 자손 인터페이스에서 따로 메소드를 구현할수도 있고요! 확장성에 용이합니다 🙂
package com.mybatis.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import com.mybatis.dto.AccountDTO;
import com.mybatis.frame.FrameMapper;
@Repository
@Mapper
public interface AccountMapper extends FrameMapper<String, AccountDTO>{
}
@Repository : DB작업중 발생되는 예외들을 Spring의 DAOExecption으로 바꿔주는 기능이 있습니다!
@Mapper : 사용하게 되면 Bean으로 등록되며 Service단에서 AutoWired로 DI하여 사용할 수 있습니다!
그리고 여기서 제네릭 타입이 실제로 어떠한것인지 구체화 시켜줍니다.
6. Service 패키지 및 클래스 생성
이제 거의 모든 준비가 끝나가네요? 이제는 좀더 구체화를 시키는 단계라고 생각하시면 됩니다!
Service의 기능을 담당할 패키지 및 클래스를 생성하시고
FrameService(인터페이스) 를 상속 받아 메소드를 오버라이드 하여 구현합니다.
@Service
public class AccountService implements FrameService<String, AccountDTO>{
@Autowired
AccountMapper mapper;
@Override
public void register(AccountDTO v) throws Exception {
mapper.insert(v);
}
@Override
public void remove(String k) throws Exception {
mapper.delete(k);
}
@Override
public void modify(AccountDTO v) throws Exception {
mapper.update(v);
}
@Override
public List<AccountDTO> get() throws Exception {
return mapper.selectAll();
}
@Override
public AccountDTO get(String k) throws Exception {
return mapper.select(k);
}
}
위에서 말했듯이 AccountMapper에서 어노테이션으로 @Mapper 를 정의해줬기에, Bean에 등록이 되고
서비스에서 @AutoWired (의존성주입) 을 통하여 객체를 생성하여 사용하는걸 볼 수 있습니다!
mapper 객체를 통해 정의된 메소드를 실행하여 결과를 받게 됩니다.
이때 여기서 말하는 mapper 객체는 AccountMapper를 의미하고, 이 Mapper인터페이스는 mapper.xml이랑 연결이 되어
실제로 쿼리문을 작성하는 곳이랑 이어져있습니다!
7. mapper.xml 생성 및 작성
http://mybatis.org/dtd/mybatis-3-mapper.dtd>">
INSERT INTO account VALUES(#{userid}, #{userpwd}, #{username}, #{usertel}, #{useremail})
UPDATE account SET userpwd=#{userpwd}, username=#{userpwd}, usertel=#{usertle}, useremail=#{useremail}
DELETE FROM account WHERE userid LIKE #{userid}
SELECT * FROM account WHERE userid LIKE #{userid}
SELECT * FROM account
<mapper> 태그로 열고 닫아서 그 안에 작업을 합니다.
여기서 각각의 쿼리문에 맞는 태그를 사용하는데, 요소로는
id = “메소드명”
resultType = “리턴값 데이터타입”
parameterType = “전달받는 매개변수 데이터타입”
입니다.
근데 잘보면 몇몇 태그들이 모두 parameterType요소를 설정하지 않은걸 확인할 수 있습니다. 이것은 mybatis가 알아서 해당 메소드에 기입되있는 매개변수값의 데이터로 설정을 해주기에 매개변수가 여러개가 아니라면 굳이 명시를 안해줘도 됩니다.
(수업때 강사님에게 질문을 했는데, 동문서답을 해주셔서 슬펐습니다. 덕분에 절대 잊지 않을게요 ^^)
http://mybatis.org/dtd/mybatis-3-mapper.dtd>">
mybatis를 적용하기 위한 코드입니다.
해당 mapper.xml이 mybatis mapper 파일이라는걸 선언합니다.
준비 끝! 테스트를 조져보자.
일단 테스트 폴더에서 테스트를 해볼 클래스를 생성해주자!
package com.mybatis.account;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import com.mybatis.dto.AccountDTO;
import com.mybatis.service.AccountService;
@SpringBootTest
class InsertTest {
@Autowired
AccountService service;
@Test
void contextLoads() {
AccountDTO dto = new AccountDTO("아이디><", "비밀번호><!", "이름!><", "010-2222-1111", "zathy1225@gmail.com");
try {
service.register(dto);
System.out.println("유저등록 성공");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@AutoWired로 service객체를 생성하고 이를 통해 실행을 해본다.
예외처리를 전이를 해줬기에 여기서 무조건 예외를 처리해줘야 한다.
결과:
호호호호홓 아주 잘되는구나!!!!!!!
나머지 update, select, delete도 똑같은 방식으로 테스트 해보면 끗!
마무리:
사실 여기까지는 너무나도 쉽고, 모듈화? 되있는 방식들입니다.
이미 정해져 있는 어노테이션, 패키지, 클래스, 인터페이스를 그에 맞게 제작하고
이름과 변수와 쿼리문만 제 입맛에 맞게 바꿔서 넣는거죠!
“다음에는 실제로 View단과 연결하여 웹페이지를 제작해보도록 하겠습니다” 두둥탁-
'Spring & Spring Boot' 카테고리의 다른 글
Spring JPA? Java Presistence API 가 뭘까요 ! (0) | 2023.02.01 |
---|---|
Spring AND Spring Boot ? (0) | 2023.01.17 |
Entity On Spring feat.JPA (0) | 2023.01.17 |
JSON을 많이 쓴다고?! on Spring Boot (0) | 2023.01.17 |
Spring 에서 ajax 사용하기 (0) | 2023.01.16 |