A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance

Yunny.Log ·2022년 5월 23일
0

Spring Boot

목록 보기
50/80
post-thumbnail

[dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: eci.server.ItemModule.entity.item.Item.materials; nested exception is org.hibernate.HibernateException: A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance: eci.server.ItemModule.entity.item.Item.materials] with root cause

문제 발생 코드

   /**
     * postupdaterequest 받아서 update 수행
     *
     * @param req
     * @return 새로 수정된 이미지
     */
    public FileUpdatedResult update(
            ItemUpdateRequest req,
            ColorRepository colorRepository,
            MemberRepository memberRepository,
            MaterialRepository materialRepository,
            ManufactureRepository manufactureRepository
    ) {
        AtomicInteger k = new AtomicInteger();
        //TODO update할 때 사용자가 기존 값 없애고 보낼 수도 있자나 => fix needed
        //isBlank 랑 isNull로 판단해서 기존 값 / req 값 채워넣기
        this.name = req.getName();
        this.type = req.getType();
        this.width = req.getWidth();
        this.height = req.getHeight();
        this.weight = req.getWeight();

        this.color = req.getColorId()==null?null:colorRepository.findById(Long.valueOf(req.getColorId()))
                .orElseThrow(ColorNotFoundException::new);

        this.materials = req.getMaterials()==null?null:
                req.getMaterials().stream().map(
                i ->
                        materialRepository.
                                findById(i).orElseThrow(MaterialNotFoundException::new)
        ).collect(
                toList()
        )
                        .stream().map(
                                r -> new ItemMaterial(
                                        this, r)
                        )
                        .collect(toList());

////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~이 부분에서 에러 발생!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        this.manufactures =
                req.getManufactures().stream().map(
                i ->
                        manufactureRepository.
                                findById(i).orElseThrow(ManufactureNotFoundException::new)
        ).collect(
                toList()
        ).stream().map(

                        //다대다 관계를 만드는 구간
                        r -> new ItemManufacture(
                                this, r, req.getPartnumbers().
                                get(k.getAndIncrement())
                        )
                )
                .collect(toList());


        ImageUpdatedResult resultImage =
                findImageUpdatedResult(
                        req.getAddedImages(),
                        req.getDeletedImages()
                );

        addImages(resultImage.getAddedImages());
        deleteImages(resultImage.getDeletedImages());

        AttachmentUpdatedResult resultAttachment =

                findAttachmentUpdatedResult(
                        req.getAddedAttachments(),
                        req.getDeletedAttachments()
                );
        addUpdatedAttachments(req, resultAttachment.getAddedAttachments());

        deleteAttachments(resultAttachment.getDeletedAttachments());

        FileUpdatedResult fileUpdatedResult = new FileUpdatedResult(resultAttachment,resultImage);

        this.modifier =
                memberRepository.findById(
                        req.getModifierId()
                ).orElseThrow(MemberNotFoundException::new);//05 -22 생성자 추가



        return fileUpdatedResult;
    }

문제 상황 : update 시 기존 엔티티의 아이를 바꾸려고 하면 에러가 뜨는 상황

문제 부분의 코드

////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~이 부분에서 에러 발생!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        this.materials = req.getMaterials()==null?null:
                req.getMaterials().stream().map(
                i ->
                        materialRepository.
                                findById(i).orElseThrow(MaterialNotFoundException::new)
        ).collect(
                toList()
        )
                        .stream().map(
                                r -> new ItemMaterial(
                                        this, r)
                        )
                        .collect(toList());

////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~이 부분에서 에러 발생!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        this.manufactures =
                req.getManufactures().stream().map(
                i ->
                        manufactureRepository.
                                findById(i).orElseThrow(ManufactureNotFoundException::new)
        ).collect(
                toList()
        ).stream().map(

                        //다대다 관계를 만드는 구간
                        r -> new ItemManufacture(
                                this, r, req.getPartnumbers().
                                get(k.getAndIncrement())
                        )
                )
                .collect(toList());

해결 후 코드

  • 기존에 존재하던 아이를 clear해줘서 아예 관계를 끊어버린 후, 새로운 아이들을 addAll을 통해서 더해주는 방식으로 코드를 변경!
        if (req.getMaterials().size()>0) {
            this.materials.clear();
            this.materials.addAll(
                    req.getMaterials().stream().map(
                                    i ->
                                            materialRepository.
                                                    findById(i).orElseThrow(MaterialNotFoundException::new)
                            ).collect(
                                    toList()
                            )
                            .stream().map(
                                    r -> new ItemMaterial(
                                            this, r)
                            )
                            .collect(toList())
            );
        }


        if(req.getManufactures().size()>0){
            this.manufactures.clear();
            this.manufactures.addAll(
                    req.getManufactures().stream().map(
                                    i ->
                                            manufactureRepository.
                                                    findById(i).orElseThrow(ManufactureNotFoundException::new)
                            ).collect(
                                    toList()
                            ).stream().map(

                                    //다대다 관계를 만드는 구간
                                    r -> new ItemManufacture(
                                            this, r, req.getPartnumbers().
                                            get(k.getAndIncrement())
                                    )
                            )
                            .collect(toList())
                    );
        }

해결 출처 :

1) https://blog.leocat.kr/notes/2016/04/26/hibernate-no-longer-referencece

list를 바꾸고 싶으면 새 list를 만들어서 set하지 말고, 내용(content)을 지우고 새로 넣자.

holder.getNames().clear();
holder.getNames().addAll(names);

2) https://stackoverflow.com/questions/5587482/hibernate-a-collection-with-cascade-all-delete-orphan-was-no-longer-referenc

writer : Manu

The method:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}
works if the parentEntity is detached and again if we update it.
But if the entity is not detached from per context, (i.e. find and update operations are in the same transaction) the below method works.

public void setChildren(Set<SonEntity> aSet) {
    //this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
    this.sonEntities.clear();
    if (aSet != null) {
        this.sonEntities.addAll(aSet);
    }
}

0개의 댓글