[bootBoard] N:1(다대일) 연관관계: 9-1. 프로젝트 적용: 게시물 등록 처리
기존 프로젝트 내용을 참고해서 브라우저에서 확인할 수 있는 코드 작성
1. DTO 계층과 서비스 계층 작성
//BoardDTO.java
package com.example.bootboard.dto;
import lombok.*;
import java.time.LocalDateTime;
@Data //getter and setter 생성
@ToString //toString 메서드 생성
@Builder //Builder 클래스를 롬복이 만들어줌
@AllArgsConstructor //모든 필드를 파라미터로 갖는 생성자를 만든다
@NoArgsConstructor //파라미터가 없는 생성자를 만든다.
public class BoardDTO {
private Long bno;
private String title;
private String content;
private String writerEmail;// 작성자 email (id)
private String writerName; //작성자 명
private LocalDateTime regDate;
private LocalDateTime modDate;
private int replyCount; //해당 게시글 댓글 수
}
//참고: @RequiredArgsConstructor는 어노테이션 아래에 있는 필드를 파라미터로 갖는 생성자를 만든다.
BoardDTO의 경우 Member에 대한 참조는 구성하지 않고 작성한다. DTO는 화면에 전달하는 데이터이거나 그 반대쪽으로 전달되는 데이터를 기준으로 하기 때문에 엔티티 클래스의 구성과 일치하지 않는 경우가 많다.
BoardDTO 클래스와 Board 엔티티 클래스의 차이점:
BoardDTO가 Member를 참조한는 대신 화면에서 필요한 작성자의 이메일과 작성자의 이름으로 처리한다는 것.
목록 화면에서도 BoardDTO를 이용하기 때문에 댓글의 개수를 의미하는 replyCount도 추가한다.
2. BoardService 인터페이스 & BoardServiceImpl 클래스 작성
- 게시물을 등록하기 위해 BoardDTO 타입을 파라미터로 전달받고, 생성된 게시물의 번호를 반환해야 한다.
- 실제 처리 과정에서 BoardDTO를 Board Entity 타입으로 변환할 필요가 있다.
- 이에 대한 처리는 BoardService 인터페이스에 dtoToEntity()를 작성해서 처리한다.
//BoardService.java
package com.example.bootboard.service;
import com.example.bootboard.dto.BoardDTO;
import com.example.bootboard.entity.Board;
import com.example.bootboard.entity.Member;
public interface BoardService {
Long register(BoardDTO dto);
//dtoToEntity() 메서드: BoardDTO를 Board엔티티 타입으로 변환한다.
default Board dtoToEntity(BoardDTO dto) {
Member member = Member.builder().email(dto.getWriterEmail()).build();
//Board 클래스는 엔티티 클래스이다.
Board board = Board.builder()
.bno(dto.getBno())
.title(dto.getTitle())
.content(dto.getContent())
.writer(member)
.build();
return board;
}
}
- DTO가 연관관계를 가진 Board 엔티티 객체와 Member 엔티티 객체를 구성해야 한다.
- 따라서 dtoToEntity()는 내부적으로 Member 엔티티를 처리하는 과정을 거친다.
- 이 때 Member는 실제 DB에 있는 이메일 주소를 사용해야 한다.
- 작성된 dtoToEntity()는 BoardServiceImpl의 register()에서 사용한다.
//BoardServiceImpl.java
package com.example.bootboard.service;
import com.example.bootboard.dto.BoardDTO;
import com.example.bootboard.entity.Board;
import com.example.bootboard.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Log4j2
public class BoardServiceImpl implements BoardService {
private final BoardRepository repository; // 자동 주입 final
@Override
public Long register(BoardDTO dto) {
log.info(dto);
//dtoToEntity() : DTO를 받아서 Entity객체로 변환. Board와 연관관계인 Member 엔티티도 처리
Board board = dtoToEntity(dto);
repository.save(board);
return board.getBno();
}
}
3. TEST : 게시물 등록
테스트를 통해 게시물을 등록한다. (아직 화면, 화면과 연결하는 로직을 만들지 않았기 때문에 DB에 등록되는지 여부로 확인한다.)
//BoardServiceTests.java
package com.example.bootboard.service;
import com.example.bootboard.dto.BoardDTO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BoardServiceTests {
@Autowired
private BoardService boardService;
@Test
public void testRegister() {
BoardDTO dto = BoardDTO.builder()
.title("Test.")
.content("Test ...")
.writerEmail("user55@aaa.com") //현재 DB에 존재한느 회원 이메일
.build();
Long bno = boardService.register(dto);
}
}
-----------------------------------------
문제 발생
2022-10-04 21:28:22.588 WARN 25748 --- [ Test worker] o.m.jdbc.message.server.ErrorPacket : Error: 1364-HY000: Field 'bno' doesn't have a default value
2022-10-04 21:28:22.590 WARN 25748 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 1364, SQLState: HY000
2022-10-04 21:28:22.590 ERROR 25748 --- [ Test worker] o.h.engine.jdbc.spi.SqlExceptionHelper : (conn=1255) Field 'bno' doesn't have a default value
could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
문제 발생 원인 :
bno에 값이 지정되어 있지 않았기 때문.
예전 버전의 mysql에는 insert시 칼럼의 값이 비워져 있으면 ' '이 자동 입력되었음
현재는 STRICT_TRANS_TABLES 때문에 오류 발생 시킴.
mysql/mariadb의 설정파일인 my.cnf 아래의 문구를 찾아서 주석처리하고 DB 재구동 할 것.
#sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
sql_mode=NO_ENGINE_SUBSTITUTION
#그러나 이 방법으로 해결 못했음.
#my.ini 열어보니 아예 이 설정이 들어있지 않았기 때문임.
# 따라서 아래의 방법으로 시도
[MySQL] Field '칼럼' doesn't have a default value 에러
[MySQL] Field '칼럼' doesn't have a default value 에러 이번 에러는 서버 이전때문에 MySQL 버전업을 하고 테스트중 발생한 에러이다. 처음에는 서버 설정이 다르거나 MySQL 버전이 다름으로서 발생한 에러인
wickedmagic.tistory.com
이 역시 성공하지 못했다. Board의 Bno의 설정을 바꾸려면 이 칼럼이 외래키로 들어 있는 Member 설정 역시 바꿔줘야 하는데, 이 부분에서 서로가 서로의 제약 역할을 하면서 수정되지 않았다. (이 프로젝트를 진행하면서 처음에 코드를 잘못 작성한 적이 있는데, 그 때문에 DB 생성 시 설정이 잘못된 것으로 추측하고 있다.) 어쨌든 시간을 쏟으면 해결할 수 있는 문제같지만 그렇게 하지 않았다. 지금 내 목표는 이 과정을 한 번 끝내는 것이지 DB설정 공부가 아니기 때문이다.
때문에 DB 자체를 삭제하고 프로젝트를 재구동하였다. tests계열의 클래스에 적은 테스트들도 전부 테스트하여 정상적인 값으로 맞춰주었다.
-----------------------------------------
3. TEST : 결과
DB정상화 후 testRegist() 테스트에 성공.
Board 테이블에 추가된 데이터