소설의 내용을 변경하는 기능 추가.
준영속 엔티티를 변경하는 방식을 나는 두 가지로 알고 있는데, 하나는 병합이고 또 다른 하나는 변경 감지이다.
병합이란 준영속 상태인 엔티티를 영속성 컨텍스트에서 조회한 다음에 변경한 값을 가지고 있는 에티티 값을 밀어 넣는 방식이다. 나는 간단하게 덮어쓰기와 비슷하다고 생각했으나, 이게 얼마나 정확한지는 모른다.
그저 merge를 쓰면 되기 때문에 매우 간단하게 보일 수 있으나, 전체 값을 가지고 변경하는 것이기 때문에 모든 속성이 변경된다는 단점이 있다. 변경한 값 외의 값이 null이 될 수 있다는 위험이다. 그러므로 변경에서는 병합이 아닌 변경 감지를 사용해야 한다.
변경 감지란 서비스 계층에서 일일히 값을 넣어주는 방식을 이용하는 것이다. 이렇게 하면 변경할 엔티티의 값을 일일히 넣어줘야 해서 null을 방지할 수 있다.
변경 감지
@Transactional
public void updateNovel(Long id, String title, String introduce, SerialState serialState, Genre genre){
Novel novel = novelRepository.findOne(id);
//작가(user)는 바꾸면 안 된다!
novel.setTitle(title);
novel.setIntroduce(introduce);
novel.setSerialState(serialState);
novel.setGenre(genre);
}
소제목과 내용을 저장하는 엔티티를 만들었다. 사실 한국어로는 소제목이지만, title이란 이름을 쓰면 소설 제목과 헷갈릴 수 있을 듯해서 BigChpater로 이름을 붙였다.
BigChapter, SmallChapter의 엔티티를 구성할 때 고민을 했는데, Chapter라는 추상 클래스를 하나 만들고 상속받는 클래스로 만들까 아니면 따로따로 만들어줄까 고민을했다.
초반에는 위의 방식대로 Chapter라는 추상 클래스를 만들었으나 문제는 BigChapter와 SmallChapter가 공유하는 내용이 별로 없다는 것이다. 해당 챕터 넘버 정도...? 그것 마저도 아직 미정이기에... 아무튼 그래서 일단 따로따로 만들어 줬다.
이름은 BigChapter이지만 실질적으로는 SmallChpater들을 묶어주는 소제목 역할을 하기에 title이 변수로 들어간다. id는 기본적으로 들어가고. 그리고 여러개의 BigChapter(없어도 된다)는 Novel과 연관된 다대일의 관계이기에 Novel과 맵핑을 해줬다. 이때 fetch는 Lazy로 뒀다. 그리고 하나의 BigChapter는 여러 개의 SmallChater와 연관되어 있기에 OneToMany로 맵핑을 했다. 이때 하위 챕터가 있는 경우 상위 챕터를 지울 수 없다는 속성을 넣기 위해 cascade 속성에 restrict가 있는지 찾아봤으나 없는 것을 확인했다. 이건 repository에서 구현해야 할 거 같다.
실질적으로 소설의 내용을 담는 엔티티이다. 그렇기에 소설의 내용인 content가 있어야 한다. 그리고 여러 개의 SmallChapter는 하나의 BigChapter와 연결되어 있어야 하기에 ManyToOne 맵핑을 했다.
소설 제목을 변경하는 테스트로 제목을 변경한 후 제목을 변경하기 이전과 비교를 하면 아래의 사진처럼 테스트 실패가 뜨는 것을 볼 수 있다. Expected는 변경 전 제목이고 Actual은 변경 후 제목이다.
만일 변경된 값과 비교를 해준다면 테스트가 잘 통과한다.
cascade 설정을 찾아보다가 든 생각인데, 소설이 변경될 경우 BigChapter가 변경되어야 하나? 물론 병경이 필요하겠지만, 자동 변경은 안 되는 걸까..? 어느 BigChapter에 다대일로 연결된 Novel이 바뀌면 연결된 BigChapter도 자동으로 바뀌는 거 아닌... 가? 아닌가...