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

丶灬走出姿态 提交于 2019-12-27 11:38:12

问题


I'm having the following issue when trying to update my entity:

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

I have a parent entity and it has a Set<...> of some children entities. When I try to update it, I get all the references to be set to this collections and set it.

The following code represents my mapping:

@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
    return this.children;
}

I've tried to clean the Set<..> only, according to this: How to "possible" solve the problem but it didn't work.

If you have any ideas, please let me know.

Thanks!


回答1:


Check all of the places where you are assigning something to sonEntities. The link you referenced distinctly points out creating a new HashSet but you can have this error anytime you reassign the set. For example:

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

Usually you want to only "new" the set once in a constructor. Any time you want to add or delete something to the list you have to modify the contents of the list instead of assigning a new list.

To add children:

public void addChild(SonEntity aSon)
{
    this.sonEntities.add(aSon);
}

To remove children:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}



回答2:


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);
    }
}



回答3:


When I read in various places that hibernate didn't like you to assign to a collection, I assumed that the safest thing to do would obviously be to make it final like this:

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

However, this doesn't work, and you get the dreaded "no longer referenced" error, which is actually quite misleading in this case.

It turns out that hibernate calls your setRoles method AND it wants its special collection class installed here, and won't accept your collection class. This had me stumped for a LONG time, despite reading all the warnings about not assigning to your collection in your set method.

So I changed to this:

public class User {
  private Set<Role> roles = null;

  public void setRoles(Set<Role> roles) {
  if (this.roles == null) {
    this.roles = roles;
  } else {
    this.roles.retainAll(roles);
   this.roles.addAll(roles);
  }
}
}

So that on the first call, hibernate installs its special class, and on subsequent calls you can use the method yourself without wrecking everything. If you want to use your class as a bean, you probably need a working setter, and this at least seems to work.




回答4:


Actually, my problem was about equals and hashcode of my entities. A legacy code can bring a lot of problems, never forget to check it out. All I've done was just keep delete-orphan strategy and correct equals and hashcode.




回答5:


I had the same error. The problem for me was, that after saving the entity the mapped collection was still null and when trying to update the entity the exception was thrown. What helped for me: Saving the entity, then make a refresh (collection is no longer null) and then perform the update. Maybe initializing the collection with new ArrayList() or something might help as well.




回答6:


HAS RELATION TYPE:


Don't try to instantiate the collection when it's declared in hasMany, just add and remove objects.

class Parent {
    static hasMany = [childs:Child]
}

USE RELATION TYPE:


But the collection could be null only when is declared as a property (use relation) and is not initialized in declaration.

class Parent {
    List<Child> childs = []
}



回答7:


I used @user2709454 approach with small improvement.

public class User {
    private Set<Role> roles;

    public void setRoles(Set<Role> roles) {
        if (this.roles == null) {
            this.roles = roles;
        } else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
            this.roles.clear();
            if(roles != null){
                this.roles.addAll(roles);
            }
        }
    }
}



回答8:


I had this problem when trying to use TreeSet. I did initialize oneToMany with TreeSet which works

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();

But, this will bring the error described at the question above. So it seems that hibernate supported SortedSet and if one just change the line above to

@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;

it works like magic :) more info on hibernate SortedSet can be here




回答9:


The only time I get this error is when I try to pass NULL into the setter for the collection. To prevent this, my setters look like this:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}



回答10:


I was getting A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instance when I was setting parent.setChildren(new ArrayList<>()). When I changed to parent.getChildren().clear(), it solved the problem.

Check for more details: HibernateException - A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance.




回答11:


I am using Spring Boot and had this issue with a collection, in spite of not directly overwriting it, because I am declaring an extra field for the same collection with a custom serializer and deserializer in order to provide a more frontend-friendly representation of the data:

  public List<Attribute> getAttributes() {
    return attributes;
  }

  public void setAttributes(List<Attribute> attributes) {
    this.attributes = attributes;
  }

  @JsonSerialize(using = AttributeSerializer.class)
  public List<Attribute> getAttributesList() {
    return attributes;
  }

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes = attributes;
  }

It seems that even though I am not overwriting the collection myself, the deserialization does it under the hood, triggering this issue all the same. The solution was to change the setter associated with the deserializer so that it would clear the list and add everything, rather than overwrite it:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }



回答12:


@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>()

I experienced the same error when I was adding child object to the existing list of Child Objects.

childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);

What resolved my problem is: child=childService.saveOrUpdate(child);

Now the child is revive with other details as well and it worked fine.




回答13:


I ran into this when updating an entity with a JSON post request. The error occurred when I updated the entity without data about the children, even when there were none. Adding

"children": [],

to the request body solved the problem.




回答14:


Adding my dumb answer. We're using Spring Data Rest. This was our pretty standard relationship. The pattern was used elsewhere.

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()


//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent

With the relationship we created, it was always intended that the children would be added through their own repo. I had not yet added the repo. The integration test we had was going through a complete lifecycle of the entity via REST calls so the transactions would close between requests. No repo for the child meant the json had the children as part of the main structure instead of in _embedded. Updates to the parent would then cause problems.




回答15:


Following solution worked for me

//Parent class
@OneToMany(mappedBy = 'parent', 
           cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()

//Updated setter of children 
public void setChildren(List<Children> children) {
    this.children.addAll(children);
    for (Children child: children)
        child.setParent(this);
}


//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;



回答16:


Instead of assigning new collection

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

Replace all elements with

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}



回答17:


One other cause may be using lombok.

@Builder - causes to save Collections.emptyList() even if you say .myCollection(new ArrayList());

@Singular - ignores the class level defaults and leaves field null even if the class field was declared as myCollection = new ArrayList()

My 2 cents, just spent 2 hours with the same :)




回答18:


be careful with

BeanUtils.copyProperties(newInsum, insumOld,"code");

This method too break the hibernate.




回答19:


It might be caused by hibernate-enhance-maven-plugin. When I enabled enableLazyInitialization property this exception started on happening on my lazy collection. I'm using hibernate 5.2.17.Final.

Note this two hibernate issues:

  • https://hibernate.atlassian.net/browse/HHH-10708
  • https://hibernate.atlassian.net/browse/HHH-11459



回答20:


Mine was completely different with Spring Boot! For me it was not due to setting collection property.

In my tests I was trying to create an entity and was getting this error for another collection that was unused!

After so much trying I just added a @Transactional on the test method and it solved it. Don't no the reason though.




回答21:


I had the same issue, but it was when the set was null. Only in the Set collection in List work find. You can try to the hibernate annotation @LazyCollection(LazyCollectionOption.FALSE) insted of JPA annotation fetch = FetchType.EAGER.

My solution: This is my configuration and work fine

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!