인프런 커뮤니티 질문&답변

이순곤님의 프로필 이미지

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

cascade = CascadeType.ALL질문

20.08.27 11:12 작성

·

5.2K

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

김영한님의 프로필 이미지
김영한
지식공유자

2020. 08. 28. 00:15

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

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

결론부터 말씀드리면 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

김영한님의 프로필 이미지
김영한
지식공유자

2021. 01. 28. 22:11

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

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

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

1

이순곤님의 프로필 이미지
이순곤
질문자

2021. 01. 28. 16:09

안녕하세요 

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

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

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

1

김영한님의 프로필 이미지
김영한
지식공유자

2021. 01. 26. 23:53

안녕하세요. 순곤님

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

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

감사합니다.

1

이순곤님의 프로필 이미지
이순곤
질문자

2020. 08. 28. 12:33

와.. 갓영한 감사합니다

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

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

감사합니당

0

이순곤님의 프로필 이미지
이순곤
질문자

2020. 08. 28. 17:14

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