ㅁ 화면에서 업로드한 첨부 파일이 내가 지정한 경로에 내가 지정한 이름으로 저장은 되었다.
- 이제 db에 insert한다.
ㅁ 현재 게시글 등록 요청이라는 하나의 기능이다. board에도 attachment에도 isnert를 해야 한다.
- 하나의 기능에 2개의 sql문을 실행해야 한다.
- 하나의 기능이기 때문에 service는 하나다. 실행할 sql문은 2개니까 2개의 dao를 호출해야 한다.
ㅁ DB
ㅁ board가 부모고 attachment가 자식이라 board에 먼저 insert를 해야 한다.
- attachment의 ref_board_no 컬럼은 board의 board_no를 참조하고 있다. (외래키 제약조건)
ㅁ 쿼리를 미리 생각해본다
- mapper 파일 안에 세미콜론은 없어야 한다.
ㅁ board-mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<mapper namespace="boardMapper">
<insert id="insertBoard">
insert
into board
(
board_no
, board_title
, board_content
)
values
(
seq_bno.nextval
, #{boardTitle}
, #{boardContent}
)
</insert>
<insert id="insertAttach">
insert
into attachment
(
file_no
, file_path
, original_name
, filesystem_name
, ref_board_no
)
values
(
seq_fno.nextval
, #{filePath}
, #{originalName}
, #{filesystemName}
, seq_bno.currval
)
</insert>
</mapper>
- mapper 파일 안에 세미콜론은 없어야 한다.
ㅁ BoardController
package com.br.file.controller;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {
private final BoardService boardService;
@PostMapping("/insert1.do")
public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
log.debug("board: {}", board); // 잘 담겼는지 확인용 로그 출력
log.debug("attach: {}", uploadFile); // 잘 담겼는지 확인용 로그 출력
AttachDto attach = null;
if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
// 전달된 파일 업로드 처리
// (1) 업로드할 폴더 (/upload/yyyyMMdd)
String filePath = "/upload/" + new SimpleDateFormat("yyyyMMdd").format(new Date());
File filePathDir = new File(filePath);
if(!filePathDir.exists()) { // 해당 경로의 폴더가 존재하지 않을 경우
filePathDir.mkdirs(); // 해당 폴더 만들기
}
// (2) 파일명 수정 작업
String originalFilename = uploadFile.getOriginalFilename(); // "xxxxx.jpg" | "xxxx.tar.gz" (파일 확장자가 2단계로 된 확장자도 있다)
// 원본명으로부터 확장자 추출하기
String originalExt = originalFilename.endsWith(".tar.gz") ? ".tar.gz"
: originalFilename.substring(originalFilename.lastIndexOf("."));
String filesystemName = UUID.randomUUID().toString().replace("-", "") + originalExt;
// (3) 업로드 (폴더에 파일 저장)
try {
uploadFile.transferTo(new File(filePathDir, filesystemName));
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
attach = AttachDto.builder()
.filePath(filePath)
.originalName(originalFilename)
.filesystemName(filesystemName)
.build();
}
int result = boardService.insertOneFileBoard(board, attach);
if(result > 0) {
log.debug("게시글 등록 성공");
}else {
log.debug("게시글 등록 실패");
}
return "redirect:/"; // 성공이든 실패든 메인페이지 요청
}
}
- 추가한 부분 : AttachDto attach 변수 선언, (4), if문 아래 부분.
- 게시글 데이터는 BoardDto에, 첨부파일 데이터는 AttachDto에 담아서 넘기면서 호출을 하면 된다.
- AttachDto attach 변수를 선언하고 null로 초기화 했다.
첨부파일은 없을 수도 있다. 없으면 null인채로 넘길 예정이다.
(4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
- 넘어온 첨부파일이 있을 때만 진행한다.
- 원래 방식이었다면 매개변수 생성자를 그때마다 만들어야 했다.
내가 원하는 필드만 골라서 값을 담을 수 있다.
일일이 매개변수 생성자를 만들 필요 없고 매개변수의 개수와 타입이 중복될 일도 없다.
이제부턴 builder 패턴을 이용한다.
롬복의 @Builder 어노테이션을 붙여서 사용 가능하다.
- build()까지 호출하면 최종적으로 AttachDto 객체가 반환된다.
- 이제 if 블록을 나온다.
넘어온 첨부파일이 있든 없든 서비스 호출을 해야 한다.
- 게시글 등록이 성공이든 실패든 메인페이지를 재요청한다.
ㅁ BoardServiceImpl
package com.br.file.service;
import java.util.List;
import org.springframework.stereotype.Service;
import com.br.file.dao.BoardDao;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {
private final BoardDao boardDao;
@Override
public int insertOneFileBoard(BoardDto board, AttachDto attach) {
int result = boardDao.insertBoard(board);
if(result > 0 && attach != null) {
result = boardDao.insertAttach(attach);
}
return result;
}
@Override
public int insertManyFileBoard(BoardDto board, List<AttachDto> list) {
return 0;
}
@Override
public List<AttachDto> selectAttachList() {
return null;
}
}
ㅁ BoardDao
package com.br.file.dao;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
@Repository
public class BoardDao {
private final SqlSessionTemplate sqlSession;
public int insertBoard(BoardDto board) {
return sqlSession.insert("boardMapper.insertBoard", board);
}
public int insertAttach(AttachDto attach) {
return sqlSession.insert("boardMapper.insertAttach", attach);
}
}
ㅁ 트랜잭션 처리
- aop 처리로 rollback 되게끔 해놨다.
- root-context.xml에 트랜잭션 처리용 aop를 위한 구문을 작성했었다.
ㅁ 실행
- 첫번째로 제목, 내용만 입력하고 등록해본다.
- 두번째로 제목, 내용, 첨부파일을 선택하고 등록해본다.