스프링부트 강좌 69강(블로그 프로젝트 ) - 댓글 작성시 Dto 사용해보기
BoardApiController.java
각각 따로따로 들고와서 진행하다 보니까 ... 프로그램이 지저분해지는 경향이 있다.
dto 를 사용하지 않은 이유는 ...? 프로그램이 거대해지면 왔다갔다 하는 데이터들이 매우 많아진다. 필드들이 많아짐. 그 많은 필드들이 조인이 되면서 넘어오는 수많은 데이터들을 모델에 있는 오브젝트로 받아서 처리하는 것은 좋은 방법이 아니다.
@PostMapping("/api/board/{boardId}/reply")
public ResponseDto<Integer> replySave(@RequestBody ReplySaveRequestDto reply) {
이렇게 하면 사용자 정보는 어디서 받아와?....detail.jsp 에서 input hidden 값으로 받아옴
DTO의 장점?
내가 필요한 데이터를 한번에 받아서 댓글쓰기에 날리고
댓글쓰기는 영속화를 시켜서 집어넣을 수 있다....
스프링부트 강좌 70강(블로그 프로젝트) - 댓글 작성시 네이티브 쿼리 사용해보기
BoardSercice.java
@Transactional
public void 댓글쓰기(ReplySaveRequestDto replySaveRequestDto) {
User user = userRepository.findById(replySaveRequestDto.getUserId()).orElseThrow(()->{
return new IllegalArgumentException("댓글쓰기 실패 : 유저 id를 찾을 수 없습니다.");
}); // 영속화 완료
Board board = boardRepository.findById(replySaveRequestDto.getBoardId()).orElseThrow(()->{
return new IllegalArgumentException("댓글쓰기 실패 : 게시글 id를 찾을 수 없습니다.");
}); // 영속화 완료
Reply reply = Reply.builder()
.user(user)
.board(board)
.content(replySaveRequestDto.getContent())
.build();
// Reply reply = new Reply();
// reply.update(user, board, replySaveRequestDto.getContent());
replyRepository.save(reply);
}
저번 시간에는 이렇게 했다. RequestDto를 받아서 내가 reply 오브젝트를 만들어서 save 하기 전에, save 하려면 reply 라는 오브젝트가 나와야 한다.
reply는 user, board 라는 오브젝트를 가져야 만들어질 수 있다. 그래서 내가 id를 받았지만 그 id를 다시 오브젝트로 변환해서 집어 넣어서 save를 했다. 굉장히 귀찮은 일이 아닐 수 없다.
JPA에서는 내가 어떤 게시글을 하나 저장할 때
BoardService.java
@Transactional
public void 글쓰기(Board board, User user) { // title, content
board.setCount(0);
board.setUser(user);
boardRepository.save(board);
}
글을 쓸때도 영속화를 시켜서 진행했다. User 오브젝트를 받아서 실행했다. 이런 방법이 잘못된 것은 아니지만 .. 귀찮아서 이런 것들을 하기 싫으면 ..
ReplyRepository에 가서 내가 직접 함수 하나를 만들면 되는데 ..
interface 안에서는 public을 적지 않아도 생략 가능함.
ReplySaveRequestDtojava 가 들고 있는
package com.yuri.blog.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ReplySaveRequestDto {
private int userId;
private int boardId;
private String content;
}
userId, boardId, content 가 순서대로 들어간다.
ReplyRepository.java
package com.yuri.blog.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.yuri.blog.dto.ReplySaveRequestDto;
import com.yuri.blog.model.Reply;
public interface ReplyRepository extends JpaRepository<Reply, Integer>{
@Query(value="INSERT INTO reply(userId, boardId, content, createDate) VALUES(?1, ?2, ?3, now())", nativeQuery = true)
void mSave(ReplySaveRequestDto replySaveRequestDto);
}
nativeQuery=true 를 설정해주면 내가 작성한 쿼리가 작동한다. 그럼 영속화 시켜서 집어넣을 필요가 없다.
BoardService.java
@Transactional
public void 댓글쓰기(ReplySaveRequestDto replySaveRequestDto) {
replyRepository.mSave(replySaveRequestDto);
}
replySaveRequestDto가 딱 들어가면서 mSave가 딱 호출이 될때 ReplySaveRequestDto가 들고있는 필드값이 순서대로 들어간다. (순서를 잘 맞춰야 함!)
하지만 워닝이 떴다. 이렇게 안들어감 !
2020-09-08 10:22:42.692 WARN 5496 --- [io-8000-exec-10] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.InvalidDataAccessApiUsageException: At least 2 parameter(s) provided but only 1 parameter(s) present in query.; nested exception is java.lang.IllegalArgumentException: At least 2 parameter(s) provided but only 1 parameter(s) present in query.]
찢어서 넣어야 함.
ReplyRepository.java
package com.yuri.blog.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.yuri.blog.dto.ReplySaveRequestDto;
import com.yuri.blog.model.Reply;
public interface ReplyRepository extends JpaRepository<Reply, Integer>{
@Query(value="INSERT INTO reply(userId, boardId, content, createDate) VALUES(?1, ?2, ?3, now())", nativeQuery = true)
void mSave(int userId, int boardId, String content);
}
BoardService.java
@Transactional
public void 댓글쓰기(ReplySaveRequestDto replySaveRequestDto) {
replyRepository.mSave(replySaveRequestDto.getUserId(), replySaveRequestDto.getBoardId(), replySaveRequestDto.getContent());
}
2020-09-08 10:30:10.218 WARN 5496 --- [nio-8000-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: S1009
2020-09-08 10:30:10.218 ERROR 5496 --- [nio-8000-exec-7] o.h.engine.jdbc.spi.SqlExceptionHelper : Can not issue data manipulation statements with executeQuery().
2020-09-08 10:30:10.228 WARN 5496 --- [nio-8000-exec-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.orm.jpa.JpaSystemException: could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet]
얘가 셀렉트 쿼리가 아니니까 셀렉트로 인식해서 .. @Transactional 을 걸어주자....!!!!!
또 같은 에로 발생 ... ResultSet 이 있어야 하는 ...?
기본적으로 jdbc가 insert, update, delete를 수행하는데 return 값을 어떻게 수행하냐면 업데이트 된 행의 개수를 리턴해준다. 1이 리턴되었으면 1개 세이브, 0 이면 안되었고, -1 이면 오류
1이 리턴되었음을 확인할 수 있다.
2020-09-08 11:02:08.196 WARN 5496 --- [nio-8000-exec-8] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.dao.InvalidDataAccessApiUsageException: Modifying queries can only use void or int/Integer as return type!; nested exception is java.lang.IllegalArgumentException: Modifying queries can only use void or int/Integer as return type!]
모디파잉은 int만 가느응 ..
-이 글은 유투버 겟인데어의 스프링 부트 강좌를 바탕으로 정리한 내용입니다.-