• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

cascade = CascadeType.ALL질문

20.08.27 11:12 작성 조회수 4.77k

3

안녕하세요 

게시판에서 게시글과 업로드의 관계에서 제 생각하고 다르게 잘 안먹혀서 질문드리겠습니다.

Posts.java
@OneToMany
(mappedBy = "posts" , cascade = CascadeType.ALL)
private List<Upload> uploadList = new ArrayList<Upload>();
Upload.java
@ManyToOne
@JoinColumn(name = "POSTS_ID")
private Posts posts;

게시글하고 업로드의 관계는 업로드가 완전히 게시글에 종속되어서

게시글 올릴때 업로드도 다같이 되고 게시글 지우면 fk게시글no에 맞춰 업로드도 다 지우게 해서 잘되는데

문제는 수정할때 업로드를 따른거하다가 취소하면 기존꺼를 남겨야하니까

저는 지금 화면에서 업로드한거 삭제하면 화면에서만 안보이게 하고 수정하기 버튼을 누르면 기존꺼 업로드는 다 삭제하고 업로드를 다시하는식으로 하는데 문제는 서비스단에서

@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {

Posts posts = postsRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다.id=" + id));

if(posts.getUploadList().size() > 0) {
List<Upload> uploadList = posts.getUploadList();
for (Upload upload : uploadList) {
Upload entity = uploadRepository.findById(upload.getId()).orElseThrow(()-> new IllegalArgumentException("해당 파일은 업습니다. id="+id));
uploadRepository.delete(entity);
}
}
Posts entity = requestDto.toEntity();
posts.update(requestDto.getTitle(), requestDto.getContent(), entity.getUploadList());

return id;
}

  uploadRepository.delete(entity); 삭제를해도 안먹히더라고요  CascadeType.ALL는 무조건 post가 지워질때만 upload도 지워지나요?

답변 6

·

답변을 작성해보세요.

6

안녕하세요. 이순곤님^^

다들 한번쯤 경험하는 문제입니다. ㅎㅎ

결론부터 말씀드리면 upload를 삭제해도, post->upload 연관관계가 cascade로 남아있기 때문에 삭제가 안됩니다.

em.remove()와 post->upload의 cascade 연관관계가 서로 충돌하는 것이지요. 하나는 지우려 하고, 하나는 cascade 관계인데 연관관계에 객체가 남아있으니 저장하려고 하고... 그래서 삭제되지 않고, 남아있습니다.

해결방법은 post->upload의 연관관계를 다음처럼 끊어주면 됩니다.

post.getUploadList().remove(findUpload);

그려면 연관관계가 없으니 cascade의 효과가 사라지겠지요^^

추가로 다음 글도 읽어보시면 도움이 되실꺼에요: https://www.inflearn.com/questions/31969

전체 테스트 코드도 남겨드립니다.

package com.example.cascadetest;

import com.example.cascadetest.entity.Post;
import com.example.cascadetest.entity.Upload;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import javax.persistence.EntityManager;

@DataJpaTest
class CascadeTestApplicationTests {

    @Autowired
    EntityManager em;

    @Test
    void cascade() {
        //given
        Post post = new Post();
        Upload upload = new Upload();
        post.addUpload(upload);
        em.persist(post);

        em.flush();
        em.clear();

        //when
        Post findPost = em.find(Post.class, post.getId());
        Upload findUpload = findPost.getUploadList().get(0);

        //cascade 때문에 객체 연관관계 남아있으면 삭제 안됨,
        //객체 연관관계가 남아있으면, cascade 때문에 post -> upload 관계가 남아있다고 생각해서 삭제가 안됨
        findPost.getUploadList().remove(findUpload);

        //em.remove을 생략하려면 post -> upload 관계에 orphanRemoval=true 추가 필요
        em.remove(findUpload);

        em.flush();
        em.clear();

        //then: upload는 제거되어야 한다.
        Upload removedUpload = em.find(Upload.class, upload.getId());
        Assertions.assertThat(removedUpload).isNull();
    }

}

도움이 되셨길 바래요^^

1

아~ 이제 과거 댓글도 메일로 오네요 ㅎㅎ

이직 성공하셨다니 축하합니다.

즐거운 코딩생활 되세요^^

1

이순곤님의 프로필

이순곤

질문자

2021.01.28

안녕하세요 

아닙니다 ㅋㅋ 해결했습니다 덕분에 마이바티스 쓰던 개인 포폴 jpa로 전환하면서 이직 성공해서 

지금은 자바스크립트 파이썬 갈아타서 노드js 리덕스 쓰고 있네요 ㅋㅋ 

항상 좋은 강의 감사했습니다 

1

안녕하세요. 순곤님

답글이 오래되면 메일로 안와서 확인이 많이 늦었습니다.

해당 오류는 다른 문제인 듯 합니다. 혹시 아직까지 해결이 안되고 고민이 남아있으시면 새로 질문글 올려주시면 도움을 드릴게요.

감사합니다.

1

이순곤님의 프로필

이순곤

질문자

2020.08.28

와.. 갓영한 감사합니다

강의다 명품인데 답글도 명품이시네요.. 

이런 묘수가 있다니 감탄하고 갑니다

감사합니당

0

이순곤님의 프로필

이순곤

질문자

2020.08.28

TT 다시 한번 여쭤봐서 죄송합니다만

삭제는 잘되는데 왜 업데이트가 안되는지 이해가 안되네여

@Transactional
public Long update(Long id, PostsUpdateRequestDto requestDto) {

Posts posts = postsRepository.findById(id).orElseThrow(() -> new IllegalArgumentException("해당 게시글이 없습니다.id=" + id));

if(posts.getUploadList().size() > 0) {
List<Upload> uploadList = posts.getUploadList();
uploadList.clear();
}

Posts entity = requestDto.toEntity();
posts.update(requestDto.getTitle(), requestDto.getContent(), entity.getUploadList());

return id;

@Getter
@NoArgsConstructor
@Entity
@Table(name = "POSTS")
public class Posts extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(length = 500, nullable = false)
private String title;

@Column(columnDefinition = "TEXT", nullable = false)
private String content;

private String author;

@OneToMany(mappedBy = "posts" , cascade = CascadeType.ALL , orphanRemoval = true)
private List<Upload> uploadList = new ArrayList<Upload>();

@OneToMany(mappedBy = "posts" , cascade = CascadeType.ALL)
private List<Like> likeList = new ArrayList<Like>();

public void addUpload(Upload upload) {
uploadList.add(upload);
upload.setPosts(this);
}

public void addlike(Like like) {
likeList.add(like);
like.setPosts(this);
}

@Builder()
public Posts(String title, String content, String author,List<PostsUploadRequestDto> postsUploadRequestDto){
this.title = title;
this.content = content;
this.author = author;

if(postsUploadRequestDto.size() > 0 ){
for(PostsUploadRequestDto attachedFile : postsUploadRequestDto){
System.out.println("line 59 : " + attachedFile.getFileName());
Upload upload = attachedFile.toEntity();
addUpload(upload);
}
}
}

public void update(String title, String content){
this.title = title;
this.content = content;
}

public void update(String title, String content, List<Upload> uploadList){
this.title = title;
this.content = content;
this.uploadList = uploadList;
}

}

Post와 Upload 관계에서 @OneToMany(mappedBy = "posts" , cascade = CascadeType.ALL , orphanRemoval = true)추가해서

기존 post.no에 해당하는 upload uploadList.clear();를 통해 다 지우고 업데이트를 통해 들어온 새로운 upload를 영속성컨텍스트에 아직 엔티티가 포함되어있어서 entity 객체 값만 바꾸면 트랙잭션 끝나는 시점에 해당 테이블 변경분 반영하는데 더티체킹통해서 근데 

org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.leesungon.book.springboot.domain.posts.upload.Upload.posts -> com.leesungon.book.springboot.domain.posts.Posts

이런 에러가 뜨는데 왜 그럴까요TT