초기 설정
project 생성 설정
- MySQL Driver, MyBatis Framework 추가 선택하기

pom.xml 설정
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
apllication.yml 세팅하기
- 기존 application.properties 에서 application.xml 로 변경한 후 아래 코드 기입
- yml 파일의 경우 겹치는 부분을 자동으로 기입해준다.
주의할점은 띄어쓰기도 모두 맞아야 한다.
그래서 무조건 자동완성 되도록 사용 하는게 좋다.
server:
port: 9020
spring:
mvc:
view:
prefix: /WEB-INF/
suffix: .jsp
devtools:
livereload:
enabled: true
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/스키마이름?serverTimezone=Asia/Seoul
username: root
password: 비밀번호
mybatis:
type-aliases-package: data.model.*
mapper-locations:
- /mapper/**/*.xml
예제 진행
SpringBootMybatis4Application
package boot.mvc.sist;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("data.model.*")
@MapperScan("data.model.mapper")
public class SpringBootMybatis4Application {
public static void main(String[] args) {
SpringApplication.run(SpringBootMybatis4Application.class, args);
}
}
mapper
MarketSql.xml
- Dao를 사용하지 않을 예정으로 쿼리문의 id가 메소드명이 된다. / sql의 mybatis와 이름(id)가 같아야한다.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="data.model.mapper.MarketMapperInter">
<select id="getTotalCountOfMarket" resultType="int">
select count(*) from market
</select>
<select id="getAllSangpums" resultType="mdto">
select * from market order by sang asc
</select>
<insert id="insertMarket" parameterType="mdto">
insert into market (sang,price,photoname,ipgoday) values(#{sang},#{price},#{photoname},now())
</insert>
<delete id="deleteMarket" parameterType="String">
delete from market where num=#{num}
</delete>
<select id="getData" parameterType="String" resultType="mdto">
select * from market where num=#{num}
</select>
<update id="updateMarket" parameterType="mdto">
update market set sang=#{sang},price=#{price},photoname=#{photoname}
</update>
</mapper>
* 주의사항 : DOCTYPE 에 빨간 밑줄이 사라지지 않을 경우 아래그림과 같이 'Download Artifact javadoc' 체크해주기.

MarketDto.java
- Spring Framework의 mybatis-config.xml 에서 설정해주던 alias를 여기에서는 Dto 에서 @Alias("이름") Annotation을 이용하여 적용해준다.
package data.model.dto;
import java.sql.Timestamp;
import org.apache.ibatis.type.Alias;
import lombok.Data;
@Data
@Alias("mdto")
public class MarketDto {
private String num;
private int price;
private String sang;
private String photoname;
private Timestamp ipgoday;
}
MarketMapperInter.java
- 기존 dao가 했던 sql문 전달자 역할을 Mapper 를 통해 여기에서 한다.
package data.model.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import data.model.dto.MarketDto;
@Mapper
public interface MarketMapperInter {
public int getTotalCountOfMarket();
public List<MarketDto> getAllSangpums();
public void insertMarket(MarketDto dto);
public MarketDto getData(String num);
public void updateMarket(MarketDto dto);
public void deleteMarket(String num);
}
MarketController.java
package data.model.controller;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import data.model.dto.MarketDto;
import data.model.service.MarketMapperInter;
@Controller
public class MarketController {
@Autowired
MarketMapperInter mapper;
@GetMapping("/")
public String start() {
return "redirect:market/list";
}
@GetMapping("/market/list")
public ModelAndView list() {
ModelAndView model=new ModelAndView();
List<MarketDto> list=new ArrayList<>();
int totalCount=mapper.getTotalCountOfMarket();
list=mapper.getAllSangpums();
model.addObject("totalCount", totalCount);
model.addObject("list", list);
model.setViewName("market/marketlist");
return model;
}
@GetMapping("/market/writeform")
public String addform() {
return "market/addform";
}
@PostMapping("/market/add")
public String insert(@ModelAttribute MarketDto dto,
MultipartFile sangupload,
HttpSession session) {
String path=session.getServletContext().getRealPath("/save");
dto.setPhotoname(sangupload.getOriginalFilename());
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
String photo="";
if(sangupload.getOriginalFilename().equals("")) {
photo=null;
} else {
String fName=sangupload.getOriginalFilename();
fName=sdf.format(new Date())+"_"+fName;
photo=fName;
try {
sangupload.transferTo(new File(path+"/"+photo));
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
dto.setPhotoname(photo);
mapper.insertMarket(dto);
return "redirect:list";
}
@GetMapping("/market/delete")
public String delete(@RequestParam String num,HttpServletRequest request) {
String photo=mapper.getData(num).getPhotoname();
System.out.println(photo);
if(photo!=null) {
String path=request.getServletContext().getRealPath("/save");
File file=new File(path+"/"+photo);
file.delete();
}
mapper.deleteMarket(num);
return "redirect:list";
}
}
페이지 만들기(jsp)
marketlist.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>
</head>
<body>
<c:if test="${totalCount==0 }">
<div class="alert alert-danger" style="margin: 20px 20px;">
<b>저장된 상품 정보가 없습니다.</b>
</div>
</c:if>
<c:if test="${totalCount>0 }">
<div class="alert alert-info">
<b>총 ${totalCount }개의 상품이 있습니다.</b>
</div>
</c:if>
<div style="margin: 20px 20px;">
<button type="button" class="btn btn-info" onclick="location.href='writeform'">글쓰기</button>
</div>
<table class="table table-bordered" style="width: 800px;">
<caption align="top"><b>상품 목록</b></caption>
<tr>
<th width="50">번호</th>
<th width="300">상품명</th>
<th width="150">가격</th>
<th width="150">입고날짜</th>
<th width="300">수정|삭제</th>
</tr>
<c:forEach var="dto" items="${list }" varStatus="i">
<tr>
<td>${i.count}</td>
<td>
<c:if test="${ dto.photoname==null}">
<img alt="" src="../save/noimage.jpg" width="50">
${dto.sang }
</c:if>
<c:if test="${ dto.photoname!=null}">
<img alt="" src="../save/${dto.photoname }" width="50">
${dto.sang }
</c:if>
</td>
<td><fmt:formatNumber value="${dto.price }" type="currency"/></td>
<td><fmt:formatDate value="${dto.ipgoday }" pattern="yyyy.MM.dd"/></td>
<td>
<button type="button" class="btn btn-outline-warning" onclick="location.href='updateform?num=${dto.num}'">상품수정</button>
<button type="button" class="btn btn-outline-danger" onclick="location.href='delete?num=${dto.num}'">상품삭제</button>
</td>
</tr>
</c:forEach>
</table>
</body>
</html>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link href="https://fonts.googleapis.com/css2?family=Bagel+Fat+One&family=Dongle:wght@300&family=East+Sea+Dokdo&family=Gamja+Flower&family=Gowun+Dodum&family=Nanum+Gothic+Coding&family=Nanum+Pen+Script&family=Orbit&display=swap" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<title>Insert title here</title>
</head>
<body>
<form action="add" method="post" enctype="multipart/form-data">
<table class="table table-bordered" style="width: 600px;">
<caption align="top"><b>상품 정보 등록</b></caption>
<tr>
<th>상품명</th>
<td>
<input type="text" name="sang" class="form-control" required="required">
</td>
</tr>
<tr>
<th>가격</th>
<td>
<input type="text" name="price" class="form-control" required="required">
</td>
</tr>
<tr>
<th>상품 이미지</th>
<td>
<input type="file" name="sangupload" class="form-control">
</td>
</tr>
<tr>
<td colspan="2" align="center">
<button type="submit" class="btn btn-info">등록</button>
<button type="button" class="btn btn-success" onclick="location.href='list'">목록</button>
</td>
</tr>
</table>
</form>
</body>
</html>
service
Spring boot에서 jpa나 mybatis사용시 mapper에 추가적인 자바코드가 필요할 때 사용하는 공간
- java문법 처리를 controller 에서 해야하는데 Controller에서 java 로직 처리 및 mapping까지 모두 하기에 부담감 준다. 이를 보완해주기 위해 service 라는 공간을 만든다.
MarketServiceInter.java
- MarketMapperInter.java 1개로 자바개념을 다 할 수 있다면 data.model.service를 만들어줄 필요 없다
- 단지 MarketMapperInter.java 의 method들을 한번에 가져오기 위해(implement받기 위해) 생성했을뿐.. 다른 의미는 없다.
package data.model.service;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import data.model.dto.MarketDto;
@Mapper
public interface MarketServiceInter {
public int getTotalCountOfMarket();
public List<MarketDto> getAllSangpums();
public void insertMarket(MarketDto dto);
public MarketDto getData(String num);
public void updateMarket(MarketDto dto);
public void deleteMarket(String num);
}
MarketService.java
- @Service : 옛날의 dao라 생각 / mapper(sql)를 보완해주는 공간
package data.model.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import data.model.dto.MarketDto;
import data.model.mapper.MarketMapperInter;
@Service
public class MarketService implements MarketServiceInter {
@Autowired
MarketMapperInter mapperInter;
@Override
public int getTotalCountOfMarket() {
return mapperInter.getTotalCountOfMarket();
}
@Override
public List<MarketDto> getAllSangpums() {
return mapperInter.getAllSangpums();
}
@Override
public void insertMarket(MarketDto dto) {
mapperInter.insertMarket(dto);
}
@Override
public MarketDto getData(String num) {
return mapperInter.getData(num);
}
@Override
public void updateMarket(MarketDto dto) {
mapperInter.updateMarket(dto);
}
@Override
public void deleteMarket(String num) {
mapperInter.deleteMarket(num);
}
}
MarketController.java
- service를 사용할 경우 MarketController.java 에서 MarketService를 @Autowired 받아서 사용한다.
- 이 pjt의 결과값은 MarketService를 사용할때나, MarketMapperInter를 사용할때에 결과값이 동일하다.
package data.model.controller;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import data.model.dto.MarketDto;
import data.model.service.MarketService;
@Controller
public class MarketController {
@Autowired
MarketService service;
@GetMapping("/")
public String start() {
return "redirect:market/list";
}
@GetMapping("/market/list")
public ModelAndView list() {
ModelAndView model=new ModelAndView();
List<MarketDto> list=new ArrayList<>();
int totalCount=service.getTotalCountOfMarket();
list=service.getAllSangpums();
model.addObject("totalCount", totalCount);
model.addObject("list", list);
model.setViewName("market/marketlist");
return model;
}
@GetMapping("/market/writeform")
public String addform() {
return "market/addform";
}
@PostMapping("/market/add")
public String insert(@ModelAttribute MarketDto dto,
MultipartFile sangupload,
HttpSession session) {
String path=session.getServletContext().getRealPath("/save");
dto.setPhotoname(sangupload.getOriginalFilename());
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
String photo="";
if(sangupload.getOriginalFilename().equals("")) {
photo=null;
} else {
String fName=sangupload.getOriginalFilename();
fName=sdf.format(new Date())+"_"+fName;
photo=fName;
try {
sangupload.transferTo(new File(path+"/"+photo));
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
dto.setPhotoname(photo);
service.insertMarket(dto);
return "redirect:list";
}
@GetMapping("/market/delete")
public String delete(@RequestParam String num,HttpServletRequest request) {
String photo=service.getData(num).getPhotoname();
System.out.println(photo);
if(photo!=null) {
String path=request.getServletContext().getRealPath("/save");
File file=new File(path+"/"+photo);
file.delete();
}
service.deleteMarket(num);
return "redirect:list";
}
}