romworld

Spring 11 - LPROD, ATTACH 테이블 연결 (일대다 관계 ResultMap 사용) + 한글깨짐 해결하기 본문

Spring

Spring 11 - LPROD, ATTACH 테이블 연결 (일대다 관계 ResultMap 사용) + 한글깨짐 해결하기

inderrom 2023. 1. 30. 16:08

관계차수

  • 1:1
  • 1:N
  • N:M

N:M에 경우 Database에서 처리 불가


일대다의 관계를 추가해보자

 

<create.jsp>

  • multiple 속성 추가

 

 

<LprodVO.java>

  • 일대다 처리해주자
package kr.or.ddit.vo;

import java.util.Arrays;
import java.util.List;

import org.springframework.web.multipart.MultipartFile;

public class LprodVO {

	private int lprodId;
	private String lprodGu;
	private String lprodNm;
	private MultipartFile[] uploadFile;
	// 상품분류 : 첨부파일 = 1: N
	private List<AttachVO> attachVOList;
	
	public LprodVO() {
	}

	
	public LprodVO(int lprodId) {
		this.lprodId = lprodId;
	}


	public int getLprodId() {
		return lprodId;
	}

	public void setLprodId(int lprodId) {
		this.lprodId = lprodId;
	}

	public String getLprodGu() {
		return lprodGu;
	}

	public void setLprodGu(String lprodGu) {
		this.lprodGu = lprodGu;
	}

	public String getLprodNm() {
		return lprodNm;
	}

	public void setLprodNm(String lprodNm) {
		this.lprodNm = lprodNm;
	}
	
	
	public MultipartFile[] getUploadFile() {
		return uploadFile;
	}


	public void setUploadFile(MultipartFile[] uploadFile) {
		this.uploadFile = uploadFile;
	}


	public List<AttachVO> getAttachVOList() {
		return attachVOList;
	}


	public void setAttachVOList(List<AttachVO> attachVOList) {
		this.attachVOList = attachVOList;
	}


	@Override
	public String toString() {
		return "LprodVO [lprodId=" + lprodId + ", lprodGu=" + lprodGu + ", lprodNm=" + lprodNm + ", uploadFile="
				+ Arrays.toString(uploadFile) + ", attachVOList=" + attachVOList + "]";
	}

}

 

 

 

<LprodController.java>

  • createPost 매서드를 수정해준다.
  • 배열타입으로 바꾸기
  • MultipartFile[] multipartFile = lprodVO.getUploadFile();
  • 리스트에 넣어주기
/*
	요청URI : /lprod/createPost
	요청파라미터 : {"lprodId:"10","lprodGu:"P404""lprodNm:"간식류"}
	요청방식 : post
	*/
	@PostMapping("/createPost")
	public String createPost(@ModelAttribute LprodVO lprodVO,
			Model model) {
		log.info("lprodVO :" +lprodVO);
		
		MultipartFile[] multipartFiles = lprodVO.getUploadFile();
		log.info("multipartFile : " + multipartFiles);
		
		List<AttachVO> voList = new ArrayList<AttachVO>();
		int seq = 1;
		//uploadFile 정보를 통해서 attachVOList에 값들을 setting해줘야함
		//배열로부터 하나씩 파일을 꺼내오자
		for(MultipartFile multipartFile : multipartFiles) {
			AttachVO vo = new AttachVO();
			log.info("------------");
			log.info("fileName : " + multipartFile.getOriginalFilename());
			log.info("fileSize : " + multipartFile.getSize());
			log.info("contentType : " + multipartFile.getContentType()); //MIME타입
			
			Long l = multipartFile.getSize();
			
			vo.setSeq(seq++);
			vo.setFilename(multipartFile.getOriginalFilename());
			vo.setFilesize(l.intValue());
			
			voList.add(vo);
			
		}
		
		lprodVO.setAttachVOList(voList);
		
		log.info("(after) lprodVO : " + lprodVO);
		
		int result = this.lprodService.createPost(lprodVO);
		
		if(result > 0) { //입력 성공
			return "redirect:/lprod/detail?lprodGu="+lprodVO.getLprodGu();
			
		}else { // 입력 실패
			model.addAttribute("data",lprodVO);
			model.addAttribute("lprodId",lprodVO.getLprodId());
			return "/lprod/create";
		}
		
	}

 

 

<LprodServiceImpl.java>

  • createPost 메서드 수정
	// 상품분류 정보 입력
	@Override
	public int createPost(LprodVO lprodVO){
		//LPROD테이블에 INSERT
		int result =  this.lprodDao.createPost(lprodVO);
		//Attach테이블에 insert(다중insert)
		List<AttachVO> attachVOList = lprodVO.getAttachVOList();
		
		return result;
	}

 


mybatis에서 INSERT ALL 하는 방법 (다중 insert)

 

INSERT ALL
INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE) 
VALUES(2,'새롬이.jsp',12345,'',sysdate)
INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE) 
VALUES(3,'새롬이3.jsp',12346,'',sysdate)
INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE) 
VALUES(4,'새롬이4.jsp',12347,'',sysdate)
select * from dual;

 

<lprod_SQL.xml>

<!-- 다중 insert 시 update 태그를 사용
		파라미터 : List<AttachVO attachVOList
		insert, update, delete의 경우 resultType은 생략
		separator=" " : 공백으로 구분
		-->
		<update id="createPostAttach" parameterType="java.util.List">
		<!-- 
		order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
		 -->
			<selectKey resultType="int" order="BEFORE" keyProperty="seq">
				SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
			</selectKey>
			<!-- index : 반복 시 index값. 0부터 시작. -->
		<foreach collection="list" item="attachVO"
				open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
				index="idx" >
				INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE) 
				VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},'',sysdate)
			</foreach>
		</update>

 

<lprodDao.java>

//다중 insert 시
	public int createPostAttach(List<AttachVO> attachVOList) {
		return this.sqlSessionTemplate.update("lprod.createPostAttach",attachVOList);
	}

 

<lprodService.java>

 

<lprodServiceImpl.java> 수정

	// 상품분류 정보 입력
	@Override
	public int createPost(LprodVO lprodVO){
		//LPROD테이블에 INSERT
		int result =  this.lprodDao.createPost(lprodVO);
		//Attach테이블에 insert(다중insert)
		List<AttachVO> attachVOList = lprodVO.getAttachVOList();
		result = result + this.lprodDao.createPostAttach(attachVOList);
		log.info("result : " + result);
		
		return result;
	}

 


Multipart 한글깨짐 문제는

web.xml 에서 한글 처리한 필터 밑에 배치해주면 해결된다!

 

 


exERD를 이용해서 일대다 관계를 설정해보자

  • 자바와 디비를 연결해서 erd를 만들 수 있다.

 

 

pk컬럼 지정
VARCHAR2(30)으로 지정

 

 

-- 기본키, 외래키 제외하고 
--스키마구조 (컬럼,자료형,크기,제약사항,NOT NULL, CHECK) 및 데이터 복제
CREATE TABLE ATTACH_BAK
AS
SELECT * FROM ATTACH;

우선 ATTACH를 백업해두고

 

기존 ATTACH테이블 데이터를 전부 지운다.

 

ERD 에서 추가한 전사적아이디 컬럼을 추가해준다.

- 제약조건 삭제 X 표시 클릭

- 인덱스도 삭제표시 클릭 X

 

- 그 후에 열 카데고리 클릭 후 SEQ와 ETP_ID를 기본키 지정해준다

 

** 다시 제약 조건과 인덱스를 확인해보면 SEQ, ETP_ID 가 선택되어있는 것을 볼 수 있다.

 


ATTACH_BAK 테이블에 데이터를 집어넣어보자

INSERT INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID)
SELECT SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, 'P101'
FROM ATTACH_BAK;
COMMIT;

 

이제 etp_id를 추가해주자

 

<AttachVO.java>

package kr.or.ddit.vo;

import java.util.Date;

import lombok.Data;

// getter/setter를 생성시켜줌
// lombok의 힘을 빌림
// Pojo에 위반..
@Data
public class AttachVO {
	private int seq;
	private String filename;
	private int filesize;
	private String thumbnail;
	private Date regdate;
	//전사적 아이디
	private String etpId;
}

 

<lprod_SQL.xml>에도 etp_id 추가해주기

<!-- 다중 insert 시 update 태그를 사용
		파라미터 : List<AttachVO attachVOList
		insert, update, delete의 경우 resultType은 생략
		separator=" " : 공백으로 구분
		-->
		<update id="createPostAttach" parameterType="java.util.List">
		<!-- 
		order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
		 -->
			<selectKey resultType="int" order="BEFORE" keyProperty="seq">
				SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
			</selectKey>
			<!-- index : 반복 시 index값. 0부터 시작. -->
		<foreach collection="list" item="attachVO"
				open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
				index="idx" >
				INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID) 
				VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},'',sysdate,
					#{attachVO.etpId})
			</foreach>
		</update>

업로드 경로 설정

 

<LprodController.java>

  • createPost 메서드 수정
/*
	요청URI : /lprod/createPost
	요청파라미터 : {"lprodId:"10","lprodGu:"P404""lprodNm:"간식류"}
	요청방식 : post
	*/
	@PostMapping("/createPost")
	public String createPost(@ModelAttribute LprodVO lprodVO,
			Model model) {
		
		String uploadFolder
			="C:\\eclipse_202006\\workspace\\springProj\\src\\main\\webapp\\resources\\upload";
		
		// - make folder 시작 -
		File uploadPath = new File(uploadFolder , getFolder());
		log.info("uploadPath : " + uploadPath);
		
		// 만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		log.info("lprodVO :" +lprodVO);
		
		MultipartFile[] multipartFiles = lprodVO.getUploadFile();
		log.info("multipartFile : " + multipartFiles);
		
		List<AttachVO> voList = new ArrayList<AttachVO>();
		int seq = 1;
		//uploadFile 정보를 통해서 attachVOList에 값들을 setting해줘야함
		//배열로부터 하나씩 파일을 꺼내오자
		for(MultipartFile multipartFile : multipartFiles) {
			AttachVO vo = new AttachVO();
			// 실제 파일명
			String uploadFileName = multipartFile.getOriginalFilename();
			
			log.info("------------");
			log.info("fileName : " + uploadFileName);
			log.info("fileSize : " + multipartFile.getSize());
			log.info("contentType : " + multipartFile.getContentType()); //MIME타입
			
			//- 같은날 같은 이미지를 업로드 시 파일 중복 방지 시작 -
			//java.util.UUID => 랜덤값 생성
			UUID uuid = UUID.randomUUID(); //임의의 값을 생성
			//원래의 파일 이름과 구분하기 위해 _를 붙힘
			uploadFileName = uuid.toString() + "_" + uploadFileName;
			//- 같은날 같은 이미지를 업로드 시 파일 중복 방지 끝 -
			
			// 파일 객체 설계(복사할 대상 경로, 파일명)
			File saveFile = new File(uploadPath, uploadFileName);
			
			try {
				//파일 복사가 일어남
				multipartFile.transferTo(saveFile);
				
				//썸네일 처리 시작-------------------
				// 파일이 이미지인지 체킹
				if(checkImageType(saveFile)) { //이미지라면 실행
					FileOutputStream thumbnail = new FileOutputStream(
							new File(uploadPath,"s_"+uploadFileName)
							);
					//썸네일 생성(원본파일, 대상, 가로크기, 세로크기)
					Thumbnailator.createThumbnail(multipartFile.getInputStream(),
							thumbnail,100,100);
					thumbnail.close();
				}
				//썸네일 처리 끝-------------------
			}catch (IllegalStateException e) {
				log.error(e.getMessage());
				return "0";
			}catch(IOException e) {
				log.error(e.getMessage());
				return "0";
			}
			
			
			Long l = multipartFile.getSize();
			
			vo.setSeq(seq++);
			// 2023/01/30/sldlaksd_새롬이.jsp
			String filename = "/" + getFolder().replace("\\", "/") + "/" +
					uploadFileName;
			vo.setFilename(filename);
			vo.setFilesize(l.intValue());
			// 2023/01/30/s_sldlaksd_새롬이.jsp
			String thumbFileName = "/" + getFolder().replace("\\", "/") + "/s_" +
					uploadFileName;
			vo.setThumbnail(thumbFileName);
			//전사적 아이디(ex)P301)
			vo.setEtpId(lprodVO.getLprodGu());
			voList.add(vo);
		}
		
		lprodVO.setAttachVOList(voList);
		
		log.info("(after) lprodVO : " + lprodVO);
		
		int result = this.lprodService.createPost(lprodVO);
		
		if(result > 0) { //입력 성공
			return "redirect:/lprod/detail?lprodGu="+lprodVO.getLprodGu();
			
		}else { // 입력 실패
			model.addAttribute("data",lprodVO);
			model.addAttribute("lprodId",lprodVO.getLprodId());
			return "/lprod/create";
		}
		
	}

 

- 하단에 썸네일을 처리를 위한 메서드도 추가

// 썸네일 처리 전 이미지 파일의 판단
	// 용량이 큰 파일을 썸네일 처리를 하지 않으면
	// 모바일과 같은 환경에서 많은 데이터를 소비해야 하므로
	// 이미지의 경우 특별한 경우가 아니면 썸네일을 제작해야 함
	// 썸네일은 이미지만 가능함
	public static boolean checkImageType(File file) {
		/*
		  .jpeg/ .jpg(JPEG 이미지)의 MIME 타입 : image/jpeg	
		 */
		//MIME 타입을 통해 이미지 여부 확인
		try {
			//fle.toPath() : 파일 객체를 path객체로 변환
			String contentType = Files.probeContentType(file.toPath());
			log.info("contentType : " + contentType);
			// MIME 타입 정보가 image로 시작하는지 여부를 return
			return contentType.startsWith("image");
		}catch(IOException e) {
			log.error(e.getMessage());
		}
		// 이 파일이 이미지가 아닐 경우
		return false;
	}

++++

mac에서는 파일 확인이 안돼서 

함수를 바꿔서 사용함

// 썸네일 처리 전 이미지 파일의 판단
		// 용량이 큰 파일을 썸네일 처리를 하지 않으면
		// 모바일과 같은 환경에서 많은 데이터를 소비해야 하므로
		// 이미지의 경우 특별한 경우가 아니면 썸네일을 제작해야 함
		// 썸네일은 이미지만 가능함
		public static boolean checkImageType(File file) {
			/*
			  .jpeg/ .jpg(JPEG 이미지)의 MIME 타입 : image/jpeg	
			 */
			//MIME 타입을 통해 이미지 여부 확인
			try {
				//file.toPath() : 파일 객체를 path객체로 변환
//				Path path = Paths.get(file.getPath());
//				String contentType = Files.probeContentType(path);
				
				MimetypesFileTypeMap mtm = new MimetypesFileTypeMap();
			    String contentType = mtm.getContentType(file.getPath());
				
				log.info("contentType : " + contentType);
				// MIME 타입 정보가 image로 시작하는지 여부를 return
				return contentType.startsWith("image");
			}catch(Exception e) {
				log.error(e.getMessage());
			}
			// 이 파일이 이미지가 아닐 경우
			return false;
		}

마지막으로 화면 카테고리도 꾸며보자

 

 

Comments