목차
스프링부트로 개발 시 mybatis를 정말 간편하게 세팅할 수 있다. 😄
역시나 시간이 지나면 기억을 못하기 때문에 미래의 내가 보기 위해 이곳에 기록한다.
예시를 위해서 테이블을 하나 생성하고 여기에 간단한 CRUD를 하겠다.
라이브 코딩(시간 상 많이 복붙) 👹
프로젝트 생성(인텔리제이) 😄
의존성은 아래처럼 세팅
웹 개발을 할 것이니까 Spring Web, 메모리 DB 사용을 위해 h2와 getter/setter, toString 등 단순반복 줄여주는 lombok, mybatis를 사용할거니까 mybatis, view template은 Thymeleaf 설정 후 FINISH 눌러서 프로젝트 생성
테스트 테이블 생성 🤗
create table member (
member_id bigint not null AUTO_INCREMENT,
email varchar(255),
member_name varchar(255),
password varchar(255),
primary key (member_id)
);
h2 버전: H2 2.1.214 (2022-06-13)
h2 세팅방법은 다루지 않겠음(중요한 점은 스프링부트에서 의존성 주입한 h2 버전과 클라이언트 h2 버전을 맞춰줘야 함)
mybatis mapper 경로 설정 🤗
기본적으로 있는 application.properteis 대신 application.yml을 쓰겠다.(기존 application.properteis는 삭제)
application.yml파일 생성 후 아래처럼 내용 기입
mapper-locations에 스프링부트에서 mapper를 인식할 수 있도록 mapper xml파일의 경로를 적어준다.(이게 끝이다.)
경로: src/main/resources/application.yml
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/devlsy-service1
username: sa
password:
driver-class-name: org.h2.Driver
# 이 부분이 mybatis mapper 설정
mybatis:
mapper-locations: mybatis-mapper/**/*.xml
url: jdbc:h2:tcp://localhost/~/devlsy-service1(여기에서 devlsy-service1은 db명인데 아래처럼 미리 db파일이 만들어져 있어야 된다. 없으면 만들면 된다.)
src/main/resources에 mybatis-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"> <!-- dtd는 스프링부트에서 사용하는 mybatis 버전 입력 -->
<mapper namespace="com.example.mybatistest.mapper.MemberMapper">
<!-- db column(스네이크케이스)과 vo(카멜케이스)의 필드명이 상이하므로 resultmap에서 매핑해줌 -->
<resultMap id="memberMap" type="com.example.mybatistest.domain.vo.MemberVO">
<id column="member_id" property="memberId"/>
<result column="email" property="email"/>
<result column="member_name" property="memberName"/>
<result column="password" property="password"/>
</resultMap>
<!-- ※ mapper interface의 method명과 id가 일치해야 함 -->
<!-- 회원 등록 -->
<insert id="insertMember" parameterType="com.example.mybatistest.domain.vo.MemberVO" useGeneratedKeys="true" keyProperty="memberId">
INSERT INTO member (email, member_name, password)
VALUES (#{email}, #{memberName}, #{password})
</insert>
<!-- 회원 단건 조회 -->
<select id="findOneMember" parameterType="Long" resultMap="memberMap">
SELECT * FROM member
WHERE member_id = #{memberId}
</select>
<!-- 회원 목록 조회 -->
<select id="findAllMember" resultMap="memberMap">
SELECT * FROM member
</select>
<!-- 회원 수정 -->
<update id="updateMember" parameterType="com.example.mybatistest.domain.vo.MemberVO">
UPDATE member set
email = #{email},
member_name = #{memberName},
password = #{password}
WHERE member_id = #{memberId}
</update>
<!-- 회원 삭제 -->
<delete id="deleteMember" parameterType="Long">
DELETE FROM member
WHERE member_id = #{memberId}
</delete>
</mapper>
스프링부트에서는 starter 의존성에서 해당 스프링부트에 맞는 의존성을 자동으로 가져온다. 😎
VO/DTO 생성 😸
package com.example.mybatistest.domain.vo;
import com.example.mybatistest.domain.dto.MemberDto;
import lombok.*;
@Getter @Setter
@ToString
// 객체를 외부에서 함부로 생성하지 못하게 제한(생성 메서드를 이용하도록)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberVO {
private Long memberId;
private String email;
private String memberName;
private String password;
/**
* dto를 vo로 변환
* @param memberDto
* @return
*/
public static MemberVO toVO(MemberDto memberDto) {
MemberVO memberVO = new MemberVO();
memberVO.setEmail(memberDto.getEmail());
memberVO.setMemberName(memberDto.getMemberName());
memberVO.setPassword(memberDto.getPassword());
return memberVO;
}
/**
* 객체 생성 메서드
* @param email
* @param memberName
* @param password
* @return
*/
public static MemberVO createMember(String email, String memberName, String password) {
MemberVO memberVO = new MemberVO();
memberVO.setEmail(email);
memberVO.setMemberName(memberName);
memberVO.setPassword(password);
return memberVO;
}
/**
* 수정 메서드
* @param email
* @param memberName
* @param password
*/
public void updatemember(String email, String memberName, String password) {
this.email = email;
this.memberName = memberName;
this.password = password;
}
}
package com.example.mybatistest.domain.dto;
import com.example.mybatistest.domain.vo.MemberVO;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@Getter @Setter
@ToString
public class MemberDto {
private Long memberId;
private String email;
private String memberName;
private String password;
/**
* vo를 dto로 변환
* @param memberVO
* @return
*/
public static MemberDto toDto(MemberVO memberVO) {
MemberDto memberDto = new MemberDto();
memberDto.setMemberId(memberVO.getMemberId());
memberDto.setEmail(memberVO.getEmail());
memberDto.setMemberName(memberVO.getMemberName());
memberDto.setPassword(memberVO.getPassword());
return memberDto;
}
}
mapper 인터페이스 생성 😃
package com.example.mybatistest.mapper;
import com.example.mybatistest.domain.vo.MemberVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper // 제일 중요한 어노테이션(이걸 설정해야 스프링부트에서 이 클래스를 찾아서 빈으로 등록한다.)
public interface MemberMapper {
// ※ 메서드명과 mapper.xml의 id가 일치해야 한다.
// 등록
void insertMember(MemberVO memberVO);
// 단건 조회
MemberVO findOneMember(Long memberId);
// 목록 조회
List<MemberVO> findAllMember();
// 수정
void updateMember(MemberVO memberVO);
// 삭제
void deleteMember(Long memberId);
}
service 🙂
클라이언트와 송수신하는 용도인 dto와 db와 송수신 용도인 vo를 분리했다.
package com.example.mybatistest.service;
import com.example.mybatistest.domain.dto.MemberDto;
import com.example.mybatistest.domain.vo.MemberVO;
import com.example.mybatistest.mapper.MemberMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import static com.example.mybatistest.domain.dto.MemberDto.toDto;
import static java.util.stream.Collectors.toList;
@Service
@Slf4j
@RequiredArgsConstructor
public class MemberService {
private final MemberMapper memberMapper;
/**
* 등록
* @param memberVO
*/
public void save(MemberVO memberVO) {
memberMapper.insertMember(memberVO);
}
/**
* 단건 조회
* @param memberId
* @return
*/
public MemberVO findOne(Long memberId) {
return memberMapper.findOneMember(memberId);
}
/**
* 목록 조회
* @return
*/
public List<MemberDto> findAll() {
List<MemberVO> members = memberMapper.findAllMember();
// 일반 foreach 사용
// List<MemberDto> result = new ArrayList<>();
// for (MemberVO m : members) {
// MemberDto memberDto = toDto(m);
// result.add(memberDto);
// }
// java 1.8 stream api 사용
List<MemberDto> result = members.stream()
.map(m -> toDto(m)).collect(toList());
return result;
}
/**
* 수정
*/
public void updateMember(MemberVO memberVO) {
memberMapper.updateMember(memberVO);
}
/**
* 삭제
* @param memberId
*/
public void remove(Long memberId) {
memberMapper.deleteMember(memberId);
}
}
단위 테스트 🤠
서비스 클래스에 커서를 두고 윈도우 기준 ctrl + shirt + t를 누르면 JUnit 테스트 클래스를 만들 수 있다.
회원등록
기분 좋게 한번에 성공했다.
실제 DB에도 잘 저장되었다.
회원단건조회
회원목록조회
회원삭제
15번에 해당되는 loki를 삭제했기 때문에 assertThat으로 검증 시 실패가 되야 한다.
DB에는 이제 17번 회원만 남아 있는 상태이다.
이 상태에서 17번 회원을 조회 시 아래처럼 정상적으로 테스트 성공 한다.(이 회원은 있으니까)
수정테스트
DB
간단한 단위 테스트가 끝났다.(물론 더 디테일하게 해야겠지만 지금은 여기서 끝)
전체 테스트 클래스 코드
package com.example.mybatistest.service;
import com.example.mybatistest.domain.dto.MemberDto;
import com.example.mybatistest.domain.vo.MemberVO;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
@Slf4j
class MemberServiceTest {
@Autowired MemberService memberService;
@Test
public void 회원등록() throws Exception {
//givin
MemberVO member = MemberVO.createMember("test@naver.com", "good", "1234");
memberService.save(member);
//when
//then
}
@Test
public void 회원단건조회() throws Exception {
//givin
MemberVO member = memberService.findOne(15L);
//when
//then
assertThat(member.getMemberName()).isEqualTo("loki");
}
@Test
public void 회원목록조회() throws Exception {
//givin
List<MemberDto> result = memberService.findAll();
//when
//then
System.out.println("result = " + result.toString());
}
@Test
public void 회원삭제() throws Exception {
//givin
// memberService.remove(17L);
//when
//then
assertThat(memberService.findOne(17L).getMemberId()).isEqualTo(17L);
}
@Test
public void 회원수정() throws Exception {
//givin
MemberVO findMember = memberService.findOne(17L);
findMember.updatemember("update_ironMan@naver.com", "update_ironMan", "4321");
//when
memberService.updateMember(findMember);
System.out.println("findMember = " + findMember.toString());
//then
}
}
포스팅을 하기 위해 임시로 만든 프로젝트라서 Controller는 일부러 만들지 않았다.
반드시 JUnit으로 단위 테스트 하면서 개발하는 습관을 길러야 한다.
개발속도가 엄청 향상 되므로..
백엔드 먼저 싹 단위테스트 하면서 개발 후 나중에 프론트를 작업하거나 RestFul방식일 경우 API로 만들어서 제공하고 협업하면 된다.
미래에 이 포스팅을 봤을 때 너는 지금보다 2배 이상 더 발전해 있기를 기원한다.
'IT > development' 카테고리의 다른 글
[mybatis] mybatis 동적쿼리(feat. <where></where>) (0) | 2022.11.20 |
---|---|
[mybatis] foreach parameterType hashmap 예제 (54) | 2022.11.20 |
[jQuery]radio/checkbox 체크 여부 확인 (0) | 2022.11.19 |
[IDE/SVN]eclipse svn branch 생성 (0) | 2022.11.19 |
[springBoot] 페이지네이션 처리(feat. MariaDB) (0) | 2022.11.19 |