오늘은 저번시간에 이어 전체적인 복습 겸, Item(물품)에 관련한 기능들을 추가해보겠다.
추가할 사항들은 DTO, Controller, Service, ServiceImpl, Mapper, Mapper.xml, html이다.
엄청많다;;
차례대로 추가해보자.
package com.example.dto;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Item {
private long no;
private String name;
private String content;
private long quantity;
private long price;
private Date regdate;
private long imageNo; // 대표 이미지번호를 저장할 임시변수
}
package com.example.dto;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.ToString;
@Getter
@Setter
@ToString (exclude = {"filedata"})
@NoArgsConstructor
@AllArgsConstructor
public class ItemImage {
private long no;
private String filename;
private long filesize;
private byte[] filedata; //BLOB
private String filetype;
private long itemno;
private Date regdate;
}
package com.example.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.dto.Item;
import com.example.dto.ItemImage;
@Mapper
public interface ItemMapper {
// 물품전체조회
public List<Item> selectItemList();
// 이미지 물품 등록
public int insertItemImage( ItemImage obj );
// 이미지 번호가 전송되면 1개의 이미지 정보 반환 (개별이미지 1개)
public ItemImage selectItemImageOne( long no );
// 물품번호를 전송하면 해당하는 이미지번호를 반환
public List<Long> selectItemImageNo( long itemno );
}
우선적으로 물품의 전체 조회가 가능한 기능부터 추가해보았다.
<?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">
<!-- 기존의 interface ItemMapper명과 일치해야 함-->
<mapper namespace="com.example.mapper.ItemMapper">
<!-- 물품 전체조회 -->
<select id="selectItemList" resultType="com.example.dto.Item">
<!-- resultType에는 반환하고자 하는 형태가 들어가야 함. -->
SELECT * FROM item ORDER BY no DESC
</select>
<!-- 물품이미지 등록 (1장) -->
<insert id="insertItemImage" parameterType="com.example.dto.ItemImage">
INSERT INTO itemimage(filename, filesize, filedata, filetype, itemno)
VALUES(#{filename}, #{filesize}, #{filedata}, #{filetype},#{itemno})
</insert>
<!-- 이미지 한개의 주소 설정 -->
<select id="selectItemImageOne" parameterType="long" resultType="com.example.dto.ItemImage">
SELECT * FROM itemimage WHERE no = #{no}
</select>
<!-- 물품당 이미지no 조회 -->
<select id="selectItemImageNo" parameterType="long" resultType="long">
SELECT i.no FROM itemimage i WHERE itemno = #{itemno}
</select>
</mapper>
package com.example.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.example.dto.Item;
import com.example.dto.ItemImage;
// 컨트롤러에서 실행하는 클래스
@Service
public interface ItemService {
// 물품 전체조회
public List<Item> selectItemList();
// 이미지 물품 등록
public int insertItemImage(ItemImage obj);
}
package com.example.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.example.dto.Item;
import com.example.dto.ItemImage;
import com.example.mapper.ItemMapper;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService{
final ItemMapper iMapper; // 매퍼 객체 생성 @Autowired Itemmapper iMapper;
@Override
public List<Item> selectItemList() {
try {
return iMapper.selectItemList();
}
catch (Exception e) {
e.printStackTrace(); // 오류발생시 터미널에 표시
return null; // 오류발생시 null 반환
}
}
@Override
public int insertItemImage(ItemImage obj) {
try {
return iMapper.insertItemImage(obj);
}
catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
Service의 경우에는 개인 실습때는 생략하기로 했다!
package com.example.controller;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
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.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.example.dto.Item;
import com.example.dto.ItemImage;
import com.example.mapper.ItemMapper;
import com.example.service.ItemService;
@Controller
@RequestMapping(value = "/item")
public class ItemController {
@Autowired ItemService iService; // 서비스 객체 생성
@Autowired ItemMapper iMapper; // 서비스 사용하지 않고 바로 매퍼 호출.(나중에는 서비스를 통해서 할것!)
@Autowired ResourceLoader resourceLoader; // resources 폴더의 파일 불러오기
@Value("${default.image}") String defaultImage; // 기본이미지
/* ------------------------------------------------------------------------------------------------- */
// image //
// <img src="@{/item/image(no=1)}">
// String = html 파일을 표시
// ResponseEntity<byte[]> => 이미지나 동영상등을 표시
// 127.0.0.1:9090/ROOT/item/image?no=1
@GetMapping(value = "/image")
public ResponseEntity<byte[]> image( @RequestParam(name = "no", defaultValue = "0" ) long no ) throws IOException {
ItemImage obj = iMapper.selectItemImageOne(no);
HttpHeaders headers = new HttpHeaders();
if( obj != null ){ // 이미지가 존재하는 경우
if( obj.getFilesize() > 0 ){
headers.setContentType( MediaType.parseMediaType( obj.getFiletype() ) );
ResponseEntity<byte[]> response = new ResponseEntity<>( obj.getFiledata(), headers, HttpStatus.OK );
return response;
}
}
InputStream is = resourceLoader.getResource("classpath:/static/images/default.png").getInputStream(); // exception 발생됨.
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<>( is.readAllBytes(), headers, HttpStatus.OK );
}
/* ------------------------------------------------------------------------------------------------- */
// insertImage //
// /item/insertimage.do?no=7 => name값은 no이고, value값은 숫자 7이 전달됨.
// <input type="text" name="no" value="7" />
@GetMapping(value = "/insertimage.do")
public String insertimageGet(@RequestParam(name = "no", defaultValue = "0", required = false) long no,
Model model) {
if (no == 0) {
return "redirect:selectlist.do"; // 상대경로로 이동, 가장 마지막 주소만 변경해서 이동
}
model.addAttribute("itemno", no);
return "/item/insertimage"; // resources/ templates/item폴더/insertimage.html
}
@PostMapping(value = "/insertimage.do")
public String insertImagePOST( @ModelAttribute ItemImage obj,
@RequestParam(name = "file1") MultipartFile file1) throws IOException {
obj.setFilename( file1.getOriginalFilename() );
obj.setFilesize( file1.getSize() );
obj.setFiletype( file1.getContentType() );
obj.setFiledata( file1.getBytes() ); //exception발생됨.
System.out.println(obj.toString()); //확인용
int ret = iService.insertItemImage(obj);
if( ret == 1) {
return "redirect:insertimage.do?no=" + obj.getItemno();
}
return "redirect:insertimage.do?no=" + obj.getItemno();
}
/* ------------------------------------------------------------------------------------------------- */
// selectlist //
// 127.0.0.1:9090/ROOT/item/selectList.do
@GetMapping(value = "/selectlist.do")
public String selectListGet(Model model) {
// 1. 서비스를 호출하여 물품목록 받기
List<Item> list = iService.selectItemList();
// 2. model을 활용하여 view로 받은목록 전달하기
model.addAttribute("list", list);
// 3. view를 화면에 표시하기
return "/item/selectlist"; // resources/templates (/item폴더를 생성, selectlist.html을 생성)
}
/* ------------------------------------------------------------------------------------------------- */
}
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>물품 전체조회</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
</head>
<body>
<header>
<br>
<h1 class="text-center">Jungyu's ItemSelecter</h1>
</header>
<table class="table table-hover table-striped text-center" style="border: 1px solid;">
<thead>
<tr style="background-color: rgb(255, 237, 237);">
<th>물품번호</th>
<th>물품명</th>
<th>물품내용</th>
<th>물품가격</th>
<th>물품수량</th>
<th>등록일</th>
<th>이미지 등록하기</th>
</tr>
</thead>
<tbody>
<!-- 'ItemController'로부터 전달받은 "list" -->
<tr th:each="obj : ${list}">
<td th:text="${obj.no}"></td>
<td th:text="${obj.name}"></td>
<td th:text="${obj.content}"></td>
<td th:text="${obj.price}"></td>
<td th:text="${obj.quantity}"></td>
<td th:text="${obj.regdate}"></td>
<!-- 물품 번호를 받아와서 이미지를 등록하는 방식. -->
<td><a th:href="@{/item/insertimage.do(no=${obj.no})}" class="btn btn-outline-danger">이미지 등록</a></td>
</tr>
</tbody>
</table>
</body>
</html>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>물품 이미지 등록</title>
<link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
</head>
<body>
<form th:action="@{/item/insertimage.do}" method="post" enctype="multipart/form-data">
외래키(물품번호) : <input type="text" name="itemno" th:value="${itemno}" readonly/><br />
첨부 이미지 : <input type="file" name="file1" accept="image/*"/><br />
<input type="submit" value="업로드" />
</form>
<a th:href="@{/item/selectlist.do}">뒤로가기</a>
</body>
</html>
위와같이 기본적으로 물품과 관련된 기본적인 페이지들을 구성해주었다.
이번에는 이미지가 없을 시 나오는 기본이미지의 경로 설정방법이다.
@Autowired ResourceLoader resourceLoader; // resources 폴더의 파일 불러오기
먼저 controller에 리소스파일을 불러오는 ResourceLoader 기능을 추가한다.
이후 기본 이미지 파일이 있는 경로를 위와 같이 지정해준다.
추가로 @Value
어노테이션을 사용하여 항상 경로를 반복적으로 입력해야 하는 수고를 줄이는 방법도 있다.
위의 방법을 이용한다면 아래와 같이 다른 상황에서도 사용이 가능하다.
위에서 본 Value 어노테이션과 같은 느낌으로, mapper의 결과값을 내보낼 형태를 일일이 경로를 지정해주지 않아도 된다.