Spring 10일차 (SQL 내 여러 테이블에 데이터 넣기, 파일 업로드)
어제 상품코드를 자동생성 했으니 나머지 데이터를 입력받고 데이터베이스에 insert해보자
create.jsp
<p>
<input type="button" id="btnSubmit" value="등록">
</p>
createPost로 진행하기 위해 본래 이렇게 있던 input타입의 button을 submit으로 잠시 바꿔주자
/*
요청URI : /crate
요청파라미터 : {lprodNo=14, lprodGu=Z002, lprodNm=분식류}
요청방식 : post
*/
@RequestMapping(value="/create", method=RequestMethod.POST)
public ModelAndView createPost(LprodVO lprodVO) {
ModelAndView model = new ModelAndView();
// Service 인터페이스 호출
// int result = this.lprodService.createPost(lprodVO);
log.info("lprodVO : " + lprodVO);
// log.info("result : " + result);
// URl 재요청
model.setViewName("redirect:/lprod/create");
return model;
}
컨트롤러 부분도 log가 잘 찍히는지 확인 먼저 하기 위해 SQL과 연결되는 부분은 잠시 주석처리를 해주자
이미지와 같이 작성을 하고 등록버튼을 누르면
2개의 데이터가 받아와졌으면 위 주석을 풀고 insert를 실행해보자
아까 submit으로 바꿔놨기 때문에 submit도 되고 Ajax도 작동을 하니 일단 ajax는 주석으로 막아놓자
(동기방식으로 진행해보기 위함)
ServiceImpl, DAO, SQL.xml 수정
어제와 마찬가지로 2개의 테이블에 각각 넣어주어야 하기 때문에 Impl, Dao, SQL.xml을 수정해주어야한다.
Impl
@Override
public int createPost(LprodVO lprodVO) {
// 1) LPROD 테이블에 insert
int result = this.lprodDao.createPost(lprodVO);
// 2) PRODUCT 테이블에 insert
// lprodVO에서 productVOList를 뽑아
List<ProductVO> productVOList = lprodVO.getProductVOList();
// for문을 돌려 1개씩
for (ProductVO productVO : productVOList) {
// sql문을 호출
result += this.lprodDao.insertProduct(productVO);
}
return result;
}
Dao
public int insertProduct(ProductVO productVO) {
return this.sqlSessionTemplate.insert("lprod.insertProduct", productVO);
}
SQL.xml
<insert id="insertProduct" parameterType="productVO">
INSERT INTO PRODUCT(PRODUCT_ID, PNAME, UNIT_PRICE)
VALUES(
#{productId},
#{pname},
#{unitPrice}
)
</insert>
package kr.or.ddit.vo;
import lombok.Data;
@Data
public class ProductVO {
private String productId;
private String pname;
private int unitPrice;
private String description;
private String manufacturer;
private String category;
private int unitsInStock;
private String condition;
private String filename;
private int quantity;
}
ProductVO
이제 데이터를 다 집어넣고 실행해보면
요청 처리는 잘 되었고, SQL에 잘 들어갔는지 확인해보자
--LPROD_GU : P601
--PRODUCT_ID : P6011
SELECT A.LPROD_ID, A.LPROD_GU, A.LPROD_NM
, B.PRODUCT_ID, B.PNAME, B.UNIT_PRICE
FROM LPROD A, PRODUCT B
WHERE A.LPROD_GU = SUBSTR(B.PRODUCT_ID,1,4);
데이터베이스에도 잘 들어가진 모습을 볼 수 있다.
파일업로드 폼 방식 요청 처리
참고자료
MemberController에서 이어서 진행한다.
예제 1)
우선 Controller와 기본 jsp먼저 만들준다.
/* 8. 파일업로드 폼 방식 요청 처리
파일업로드 폼 파일 요소값을 스프링MVC가 지원하는 MultipartFile
매개변수로 처리함
*/
@GetMapping("/registerForm")
public String registerForm() {
log.info("registerForm에 왔다");
return "registerForm";
}
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!--
요청URI : /registerFile01
요청파라미터 : {picture=파일객체}
요청방식 : post
-->
<form action="registerFile01" method="post" enctype="multipart/form-data">
<p><input type="file" name="picture"></p>
<p><input type="submit" value="파일업로드"></p>
</form>
여기서 파일을 선택하고 업로드를 누르면 처리할 Controller를 만들어준다.
/*
요청URI : /registerFile01
요청파라미터 : {picture=파일객체}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile01")
// 스프링에서 제공하는 파일 객체
public String registerFile01(MultipartFile picture) {
log.info("registerFile01에 왔다");
// 원본 파일명
String fileName = picture.getOriginalFilename();
// 파일 크기
long size = picture.getSize();
// MINE 타입
String contentType = picture.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
return "success";
}
web.xml 설정
<!-- web.xml의 설정은 WAS(Tomcat) 자체 설정일 뿐임. -->
<!-- multipart-config : 메모리사이즈, 업로드 파일 저장 위치, 최대 크기 설정 -->
<multipart-config>
<location>c:\\upload</location><!-- 업로드 되는 파일을 저장할 공간 -->
<max-file-size>20971520</max-file-size><!-- 업로드 파일의 최대 크기 1MB * 20 -->
<max-request-size>41943040</max-request-size><!-- 한 번에 올릴 수 있는 최대 크기 40MB -->
<file-size-threshold>20971520</file-size-threshold><!-- 메모리 사용 크기 20MB -->
</multipart-config>
web.xml에 위 구문을
<!-- Processes application requests -->
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<!-- 여기에 넣어주세요 -->
</servlet>
주석처리된 부분에 넣어주고,
맨 아래 </web-app> 안쪽에 아래 코드를 넣어준다.
<!-- multipart filter 추가하기(한글 처리 다음에 넣기!!!) -->
<filter>
<display-name>springMultipartFilter</display-name>
<filter-name>springMultipartFilter</filter-name>
<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springMultipartFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
WAS 설정
servers 프로젝트를 열고 context.xml을 아래 코드로 덮어씌워준다
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
만약 WAS에 아래와 같이 설정하지 않으면 "Could not parse multipart servlet request" 에러발생,? WAS에서
Multipart를 찾지 못하게 된다
--><!-- The contents of this file will be loaded for each web application -->
<Context allowCasualMultipartParsing="true" >
<!-- 케시문제 해결 -->
<Resources cachingAllowed="true" cacheMaxSize="100000"></Resources>
<!-- Default set of monitored resources. If one of these changes, the -->
<!-- web application will be reloaded. -->
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
<!-- Uncomment this to disable session persistence across Tomcat restarts -->
<!--
<Manager pathname="" />
-->
</Context>
root-context 설정
<!-- 파일업로드 설정
CommonsMultipartResolver multipartResolver = new multipartResolver();
multipartResolver.setMaxUploadSize(10485760);
multipartResolver.setDefaultEncoding("UTF-8");
-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 파일업로드 용량 (10MB)-->
<property name="maxUploadSize" value="10485760"/> <!-- 1024 * 1024 * 10 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- 파일업로드 디렉토리 설정 -->
<bean id="uploadPath" class="java.lang.String">
<constructor-arg value="c:\\upload"/>
</bean>
root-context.xml의 맨 밑(</beans> 안 쪽)에 넣어준다.
설정이 끝났다면 저장하고 실행해보자.
예제 2)
jsp와 Controller에 아래와 같이 추가해준다.
<hr />
<!--
요청URI : /registerFile02
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile02" method="post" enctype="multipart/form-data">
<p>userId : <input type="text" name="userId" value="hongkd"></p>
<p>password : <input type="password" name="password" value="1234"></p>
<p><input type="file" value="picture"></p>
<p><input type="submit" value="파일업로드"></p>
</form>
/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을
MultipartFile 매개변수와 문자열 매개변수로 처리함
요청URI : /registerFile02
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile02")
public String registerFile02(String userId, String password, MultipartFile picture) {
log.info("registerFile02에 왔다");
log.info("userId : " + userId);
log.info("password : " + password);
// 원본 파일명
String fileName = picture.getOriginalFilename();
// 파일 크기
long size = picture.getSize();
// MINE 타입
String contentType = picture.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
return "success";
}
예제 3) 자바빈으로 담아보기
<hr />
<!--
요청URI : /registerFile03
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile03" method="post" enctype="multipart/form-data">
<p>userId : <input type="text" name="userId" value="hongkd"></p>
<p>password : <input type="password" name="password" value="1234"></p>
<p><input type="file" name="picture"></p>
<p><input type="submit" value="파일업로드"></p>
</form>
/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을
MultipartFile 매개변수와 자바빈즈 매개변수로 처리
요청URI : /registerFile03
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
*/
@ResponseBody
@PostMapping("registerFile03")
public String registerFile03(MultipartFile picture,
Member member) {
log.info("registerFile03에 왔다.");
log.info("userId : " + member.getUserId());
log.info("password : " + member.getPassword());
// 원본 파일명
String fileName = picture.getOriginalFilename();
// 파일 크기
long size = picture.getSize();
// MINE 타입
String contentType = picture.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
return "success";
}
예제 4)
이번엔 MultipartFile을 Member VO안에 넣어보도록 하자
VO에 private MultipartFile picture 를 추가해준다.
// 스프링 프레임워크에서 제공하는 MultipartFile 파일 객체 타입
private MultipartFile picture;
<!--
요청URI : /registerFile04
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile04" method="post" enctype="multipart/form-data">
<p>userId : <input type="text" name="userId" value="hongkd"></p>
<p>password : <input type="password" name="password" value="1234"></p>
<p><input type="file" name="picture"></p>
<p><input type="submit" value="파일업로드"></p>
</form>
/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을
Member 타입의 자바빈즈 매개변수로 처리
*/
@ResponseBody
@PostMapping("registerFile04")
public String registerFile04(Member member) {
log.info("registerFile04에 왔다.");
MultipartFile picture = member.getPicture();
log.info("userId : " + member.getUserId());
log.info("password : " + member.getPassword());
// 원본 파일명
String fileName = picture.getOriginalFilename();
// 파일 크기
long size = picture.getSize();
// MINE 타입
String contentType = picture.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
return "success";
}
예제 5) 여러개의 파일 업로드 폼 파일 요소 값을 여러개의 MultipartFile 매개변수로 처리
<!--
요청URI : /registerFile05
요청파라미터 : {picture=파일객체,picture2=파일객체}
요청방식 : post
-->
<form action="/registerFile05" method="post" enctype="multipart/form-data">
<p><input type="file" name="picture"></p>
<p><input type="file" name="picture2"></p>
<p><input type="submit" value="파일업로드"></p>
</form>
/*
여러 개의 파일업로드 폼 파일 요소값을 여러 개의
MultipartFile 매개변수로 처리
요청URI : /registerFile05
요청파라미터 : {picture=파일객체,picture2=파일객체}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile05")
public String registerFile05(MultipartFile picture, MultipartFile picture2) {
log.info("registerFile05에 왔다.");
// 원본 파일명
String fileName = picture.getOriginalFilename();
// 파일 크기
long size = picture.getSize();
// MINE 타입
String contentType = picture.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
// -----------------------------------------------
// 원본 파일명
fileName = picture2.getOriginalFilename();
// 파일 크기
size = picture2.getSize();
// MINE 타입
contentType = picture2.getContentType();
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
return "success";
}
예제 6) 파일이 여러개 일 때
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 Member 타입의 자바빈즈 배열 매개변수로 처리
multipartFile은 List로 담을 수 없기 때문에 배열로 열어 사용해야한다.
VO에 배열을 담을 변수를 선언해주고
// <input type="file" name="pictures" multiple>
private MultipartFile[] pictures;
multiple 요소를 추가해 여러개의 파일을 선택할 수 있게 한다.
<!--
요청URI : /registerFile08
요청파라미터 : {picture=파일객체,picture2=파일객체}
요청방식 : post
-->
<form action="/registerFile08" method="post" enctype="multipart/form-data">
<p>userId : <input type="text" name="userId" value="hongkd"></p>
<p>password : <input type="password" name="password" value="1234"></p>
<p><input type="file" name="pictures" multiple></p>
<p><input type="submit" value="파일업로드"></p>
</form>
/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을
Member 타입의 자바빈즈 배열 매개변수로
처리
요청URI : /registerFile08
요청파라미터 : {userId=a001,password=1234,pictures=파일객체들}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile08")
public String registerFile08(Member member) {
log.info("registerFile08");
log.info("userId: "+member.getUserId());
log.info("password: "+member.getPassword());
//스프링파일객체 배열 타입
MultipartFile[] picures = member.getPictures();
String fileName="";
long size=0;
String contentType="";
for(MultipartFile multipartFile:picures) {
fileName = multipartFile.getOriginalFilename();
size = multipartFile.getSize();
contentType = multipartFile.getContentType();
log.info("===========================");
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
log.info("===========================");
}
return "success";
}
첨부파일 처리
ATTACH 테이블 생성
CREATE TABLE ATTACH(
GLOBAL_CODE VARCHAR2(50), -- 전사적코드
SEQ NUMBER, -- 일련번호
FILE_NAME VARCHAR2(300), -- 원본파일경로명
FILE_SIZE NUMBER, -- 파일 크기
CONTENT_TYPE VARCHAR2(30), -- MINE 파일 타입
REG_DATE DATE, -- 등록일
CONSTRAINT PK_ATTACH PRIMARY KEY(GLOBAL_CODE)
);
AttachVO도 하나 만들어주자
package kr.or.ddit.vo;
import java.util.Date;
import lombok.Data;
@Data
public class AttachVO {
private String globalCode;
private int seq;
private String fileName;
private long fileSize;
private String contentType;
private Date regDate;
}
Alias도 생성해주고
<typeAlias type="kr.or.ddit.vo.AttachVO" alias="attachVO" />
Controller에 방금 만든 registerFile08의 내용을 일부 잘라낸 뒤 ServiceImpl에 넣어주자
/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을
Member 타입의 자바빈즈 배열 매개변수로
처리
요청URI : /registerFile08
요청파라미터 : {userId=a001,password=1234,pictures=파일객체들}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile08")
public String registerFile08(Member member) {
log.info("registerFile08");
int result = this.memberService.registerFile08(member);
log.info("register08->result : " + result);
return "success";
}
@Override
public int registerFile08(Member member) {
log.info("userId: "+member.getUserId());
log.info("password: "+member.getPassword());
// member테이블에 insert
member.setUserName("개똥이");
member.setCoin(1000);
int result = this.memberDao.registerUserId(member);
log.info("registerFile08->result(1) : " + result);
//스프링파일객체 배열 타입
MultipartFile[] picures = member.getPictures();
String fileName="";
long size=0;
String contentType="";
int seq = 1;
for(MultipartFile multipartFile:picures) {
fileName = multipartFile.getOriginalFilename();
size = multipartFile.getSize();
contentType = multipartFile.getContentType();
log.info("===========================");
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
log.info("===========================");
// 파일 업로드 처리
// 2) Attach 테이블에 인서트
AttachVO attachVO = new AttachVO();
attachVO.setGlobalCode(member.getUserId());
attachVO.setSeq(seq++); // 들어간 뒤 증가
attachVO.setFileName(fileName);
attachVO.setFileSize(size);
attachVO.setContentType(contentType);
attachVO.setRegDate(null);
result += this.attachDao.insertAttach(attachVO);
}
return result;
}
AttachVO 전용 Dao도 하나 만들어주어야한다.(모든 첨부파일은 여기서 처리하려고 함)
package kr.or.ddit.service.dao;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import kr.or.ddit.vo.AttachVO;
@Repository
public class AttachDao {
@Autowired
SqlSessionTemplate sqlSessionTemplate;
public int insertAttach(AttachVO attachVO) {
return this.sqlSessionTemplate.insert("attach.insertAttach",attachVO);
}
}
SQL.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">
<mapper namespace="attach">
<insert id="insertAttach" parameterType="attachVO">
INSERT INTO ATTACH(GLOBAL_CODE, SEQ, FILE_NAME, FILE_SIZE, CONTENT_TYPE, REG_DATE)
VALUES(#{globalCode}, #{seq}, #{fileName}, #{fileSize}, #{contentType}, SYSDATE)
</insert>
</mapper>
※ 자 안됀다 쌤~
복합키 설정을 해준 뒤 userId를 다른걸 넣고 실행해보자.
파일 업로드 처리
ServiceImpl에 추가해준다.
@Autowired
String uploadFolder;
// 연/월/일 폴더 생성
public String getFolder() {
// 2022-11-16 형식(format) 지정
// 간단한 날짜 형식
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 날짜 객체 생성(java.util 패키지)
Date date = new Date();
// 2022-11-16
String str = sdf.format(date);
// 2024-01-30 -> 2024\\01\\30
return str.replace("-", File.separator);
}
위 // 파일 업로드 주석부분에 아래 코드를 추가해준다.
@Override
public int registerFile08(Member member) {
log.info("userId : " + member.getUserId());
log.info("password : " + member.getPassword());
// 업로드 대상 경로
log.info("uploadFolder : " + uploadFolder);
// make folder 시작 ////////////
File uploadPath = new File(uploadFolder, getFolder());
log.info("uploadPath" + uploadPath);
// 만약 연/월/일 해당 폴더가 없으면 생성
if(uploadPath.exists() == false) {
uploadPath.mkdirs();
}
// make folder 끝 ////////////
// member테이블에 insert
member.setUserName("개똥이");
member.setCoin(1000);
int result = this.memberDao.registerUserId(member);
log.info("registerFile08->result(1) : " + result);
//스프링파일객체 배열 타입
MultipartFile[] picures = member.getPictures();
String fileName="";
long size=0;
String contentType="";
int seq = 1;
for(MultipartFile multipartFile:picures) {
fileName = multipartFile.getOriginalFilename();
size = multipartFile.getSize();
contentType = multipartFile.getContentType();
log.info("===========================");
log.info("fileName : " + fileName);
log.info("size : " + size);
log.info("contentType : " + contentType);
log.info("===========================");
// 파일 업로드 처리
///////////// 같은 날 같은 이미지명을 업로드 시 파일 중복 방지 시작
// java.util.UUID -> 랜덤값 생성
UUID uuid = UUID.randomUUID();
// 3asd53a15asd_개똥이.jpg
fileName = uuid.toString() + "_" + fileName;
///////////// 같은 날 같은 이미지명을 업로드 시 파일 중복 방지 끝
// File 객체 설계(복사할 대상 경로, 파일명)
// c:\\ ... \\upload\\2024\\05\\03\\3asd53a15asd_개똥이.jpg
File saveFile = new File(uploadPath, fileName);
// 2) Attach 테이블에 인서트
AttachVO attachVO = new AttachVO();
attachVO.setGlobalCode(member.getUserId());
attachVO.setSeq(seq++); // 들어간 뒤 증가
attachVO.setFileName(fileName);
attachVO.setFileSize(size);
attachVO.setContentType(contentType);
attachVO.setRegDate(null);
result += this.attachDao.insertAttach(attachVO);
}
return result;
}