Spring boot Mybatis 활용

limchard·2023년 11월 1일
0

spring boot

목록 보기
5/6

초기 설정

project 생성 설정

  • MySQL Driver, MyBatis Framework 추가 선택하기

pom.xml 설정

  • jstl, tomcat 추가하기
<!-- https://mvnrepository.com/artifact/jstl/jstl -->
		<dependency>
		    <groupId>jstl</groupId>
		    <artifactId>jstl</artifactId>
		    <version>1.2</version>
		</dependency>
		<!-- 기본 라이브러리에도 jstl 이 들어가있지만.. 이걸 넣어줘야 더 잘먹고.. 오류도 더 디테일하게 잡아준다고 합니다요. -->
		
		<!-- https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-jasper -->
		<dependency>
		    <groupId>org.apache.tomcat.embed</groupId>
		    <artifactId>tomcat-embed-jasper</artifactId>
		    <!-- <version>9.0.69</version>--> <!-- 버전이 애매하면 아래 scope로 변경해줘라. -->
		    <scope>provided</scope>
		</dependency>

apllication.yml 세팅하기

  • 기존 application.properties 에서 application.xml 로 변경한 후 아래 코드 기입
  • yml 파일의 경우 겹치는 부분을 자동으로 기입해준다.
    주의할점은 띄어쓰기도 모두 맞아야 한다.
    그래서 무조건 자동완성 되도록 사용 하는게 좋다.
# tomcat port
server:
  port: 9020

# jsp
spring:
  mvc:
    view:
      prefix: /WEB-INF/
      suffix: .jsp

  devtools:
    livereload:
      enabled: true
      
#mysql
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/스키마이름?serverTimezone=Asia/Seoul
    username: root
    password: 비밀번호
# project에 대한 기본 정보값들을 담아두는 것이다.

# mybatis mapper 등록!
mybatis:
  type-aliases-package: data.model.* # 이건 사실 dao 이고.. Spring boot에서는 주로 mapper라는 표현을 쓰기 때문에 mapper라고 표현한거다.
  #type-aliases-package: data.model.dto,data.model.mapper,
  mapper-locations: # mapper에 오는 실제 xml 파일들을 위치할 것이다.
  - /mapper/**/*.xml # 이게 Spring framework에 있었던 mapper이다.. 
  
  
  # sql문과 dao를 연결해주는것을 @Mapper 라는걸 쓴다... 기존에 파일 이름을 mapper 라고 한것은 그냥 단순히 보기 편하라고..
  


예제 진행

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") // mapper 등록... 이 위치에 mapper가 위치한다!! 
								// 기존에 bean에 등록하던거 대신해서 interface에 해당하는 것 등록함.
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"> <!-- 여기서의 id는 MarketMapperInter.java 에서 method 이름이다. -->
		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 // lombok 적용
@Alias("mdto") // spring framework 에서는 config 에서 따로 지정해줬던거를 여기서는 이렇게 간단하게 설정 가능하다!!
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 // dao가 했던 sql 전달자 역할을 여기서 한다.
public interface MarketMapperInter {

	public int getTotalCountOfMarket(); // getTotalCountOfMarket이 id(framework에서 id로 썼던거) 역할을 해준다.
	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"; // 이렇게 하면 아래 port번호만 입력해서 list가 나온다.
	}
	
	@GetMapping("/market/list")
	public ModelAndView list() {
		
		ModelAndView model=new ModelAndView();
		List<MarketDto> list=new ArrayList<>();
				
		// db 호출
		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) {

		// upload할 save 위치 구하기
		String path=session.getServletContext().getRealPath("/save");
					
		// upload한 파일 dto 얻기
		dto.setPhotoname(sangupload.getOriginalFilename()); // 이거 물어봐야겠다.
		
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
					
		String photo="";

		if(sangupload.getOriginalFilename().equals("")) {
			photo=null;
			// null 은 equals가 먹지 않음. 이유는? equals는 String 비교 메서드이기 때문이다.
			
		} else {
			String fName=sangupload.getOriginalFilename();
			fName=sdf.format(new Date())+"_"+fName;
			photo=fName;
				
			// upload 하기
			try {
				sangupload.transferTo(new File(path+"/"+photo));
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// dto의 Photo에 업로드 한 것을 넣어줘야 한다.
		dto.setPhotoname(photo);
		
		// insert (db에 저장)
		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>

addform.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>

<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() {
		// TODO Auto-generated method stub
		return mapperInter.getTotalCountOfMarket();
	}

	@Override
	public List<MarketDto> getAllSangpums() {
		// TODO Auto-generated method stub
		return mapperInter.getAllSangpums();
	}

	@Override
	public void insertMarket(MarketDto dto) {
		// TODO Auto-generated method stub
		mapperInter.insertMarket(dto);
	}

	@Override
	public MarketDto getData(String num) {
		// TODO Auto-generated method stub
		return mapperInter.getData(num);
	}

	@Override
	public void updateMarket(MarketDto dto) {
		// TODO Auto-generated method stub
		mapperInter.updateMarket(dto);
	}

	@Override
	public void deleteMarket(String num) {
		// TODO Auto-generated method stub
		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.MarketMapperInter;
import data.model.service.MarketService;

@Controller
public class MarketController {
	
//	
//	@Autowired
//	MarketMapperInter mapper;
//	
	@Autowired
	MarketService service;
	
	@GetMapping("/")
	public String start() {
		return "redirect:market/list"; // 이렇게 하면 아래 port번호만 입력해서 list가 나온다.
	}
	
	@GetMapping("/market/list")
	public ModelAndView list() {
		
		ModelAndView model=new ModelAndView();
		List<MarketDto> list=new ArrayList<>();
				
		// db 호출
		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) {

		// upload할 save 위치 구하기
		String path=session.getServletContext().getRealPath("/save");
					
		// upload한 파일 dto 얻기
		dto.setPhotoname(sangupload.getOriginalFilename()); // 이거 물어봐야겠다.
		
		SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMddHHmmss");
					
		String photo="";

		if(sangupload.getOriginalFilename().equals("")) {
			photo=null;
			// null 은 equals가 먹지 않음. 이유는? equals는 String 비교 메서드이기 때문이다.
			
		} else {
			String fName=sangupload.getOriginalFilename();
			fName=sdf.format(new Date())+"_"+fName;
			photo=fName;
				
			// upload 하기
			try {
				sangupload.transferTo(new File(path+"/"+photo));
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		// dto의 Photo에 업로드 한 것을 넣어줘야 한다.
		dto.setPhotoname(photo);
		
		// insert (db에 저장)
		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";
	}
}
profile
java를 잡아...... 하... 이게 맞나...

0개의 댓글