Hibernate - Uma coleção com cascade = ”all-delete-orphan” não era mais referenciada pela instância da entidade proprietária

225

Estou com o seguinte problema ao tentar atualizar minha entidade:

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

Eu tenho uma entidade pai e ela tem uma Set<...>de algumas entidades filhas. Quando tento atualizá-lo, recebo todas as referências a serem configuradas para essas coleções e defini-lo.

O código a seguir representa meu mapeamento:

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

Tentei limpar apenas o Set <..>, de acordo com o seguinte: Como "possível" resolver o problema, mas não funcionou.

Se você tiver alguma idéia, entre em contato.

Obrigado!

axcdnt
fonte
1
@ mel3kings, o link que você forneceu não está mais ativo.
Opala
tente usar coleções mutáveis ​​ao remover elementos. Por exemplo, não use something.manyother.remove(other)if manyotheré a List<T>. Faça manyother mutável, como ArrayList<T>e usoorphanDelete = true
nurettin

Respostas:

220

Verifique todos os lugares onde você está atribuindo algo para sonEntities. O link que você referiu distintamente indica a criação de um novo HashSet, mas você pode ter esse erro sempre que redesignar o conjunto. Por exemplo:

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

Geralmente você deseja "novo" apenas o conjunto uma vez em um construtor. Sempre que você deseja adicionar ou excluir algo da lista, você deve modificar o conteúdo da lista em vez de atribuir uma nova lista.

Para adicionar filhos:

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

Para remover crianças:

public void removeChild(SonEntity aSon)
{
    this.sonEntities.remove(aSon);
}
brainimus
fonte
8
Na verdade, meu problema era sobre iguais e hashcode das minhas entidades. Um código legado pode trazer muitos problemas, nunca se esqueça de conferir. Tudo o que fiz foi apenas manter a estratégia de excluir órfãos e corrigir os iguais e o código de hash.
axcdnt
6
Estou feliz que você resolveu seu problema. equals e hashcode me morderam algumas vezes com o Hibernate. Em vez de atualizar o título da pergunta com "[Resolvido]", você deve prosseguir, postar sua resposta e marcá-la como a resposta aceita.
brainimus
Obrigado, encontrei algo parecido e descobri que meu setter estava vazio. #
Siddhartha
sim, mesmo em listas vazias, você recebe esse erro. eu não esperaria isso porque não há órfãos (a lista estava vazia).
Tibi
4
Geralmente, você deseja "novo" apenas o conjunto uma vez em um construtor. Sempre que você deseja adicionar ou excluir algo da lista, você deve modificar o conteúdo da lista em vez de atribuir uma nova lista. A maioria imp
Nikhil Sahu
109

O método:

public void setChildren(Set<SonEntity> aSet) {
    this.sonEntities = aSet;
}

funciona se o parentEntityestiver desanexado e novamente se o atualizarmos.
Porém, se a entidade não for desanexada por contexto, (ou seja, as operações de localização e atualização estão na mesma transação), o método abaixo funcionará.

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);
    }
}
Manu
fonte
1
@ Skuld Eu tenho um problema semelhante e apliquei sua solução (no método setter, limpei a coleção children - this.children.clear () - e adicionei os novos filhos - this.children.addAll (children)). Essa alteração não resolveu meu problema. Ainda recebo a exceção "Uma coleção com cascade =" all-delete-orphan "não era mais referenciada pela instância da entidade proprietária". Você tem uma ideia do porquê? Muito obrigado!
ovdsrn
@ovdsrn Desculpe, esta não é a minha resposta, resolvi a formatação da resposta, o autor original (kmmanu) poderá ajudá-lo (ou, se você quiser iniciar uma nova pergunta, se você preferir iniciar uma nova pergunta, se o cenário for diferente de a pergunta original feita aqui) Boa sorte
Skuld
1
Isso funciona bem, mas não é tão bom quando os filhos estão contidos em um componente de hibernação aninhado e o componente é tornado nulo em sua Entidade. Então você recebe o mesmo erro. Isso ocorre porque o proprietário dos filhos é a Entidade raiz e não o componente que é tornado nulo ... Sendo assim, um componente nunca pode se tornar nulo, mas deve resultar em um método de encaminhamento para um destroy () no criança ... pelo menos, eu não sei uma solução melhor ... é uma espécie de coleção construção clear (), mas, em seguida, em um componente através destroy () ...
edbras
Veja também esta publicação para obter mais detalhes acima: stackoverflow.com/questions/4770262/…
edbras
7
use this.sonEntities.retainAll (aSet) sobre sonEntities.clear () porque se aSet == this.sonEntities (ou seja, o mesmo objeto), você limpará o conjunto antes de adicioná-lo!
Martin Martin
33

Quando li em vários lugares que o hibernate não gostaria que você atribuísse a uma coleção, presumi que a coisa mais segura a fazer seria obviamente torná-la final assim:

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

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

No entanto, isso não funciona, e você recebe o temido erro "não mais referenciado", o que é realmente bastante enganador neste caso.

Acontece que o hibernate chama seu método setRoles E deseja que sua classe de coleção especial seja instalada aqui e não aceita sua classe de coleção. Isso me deixou perplexo por muito tempo, apesar de ler todos os avisos sobre não atribuir à sua coleção no seu método definido.

Então eu mudei para isso:

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

Para que, na primeira chamada, o hibernate instale sua classe especial e, nas chamadas subsequentes, você possa usar o método sem destruir tudo. Se você quiser usar sua classe como um bean, provavelmente precisará de um levantador de trabalho, e isso pelo menos parece funcionar.

xpusostomos
fonte
2
Por que você está chamando reterAll ()? Por que não clear () seguido por addAll ()?
edbras
1
"Por que você está chamando keepAll ()? Por que não clear () seguido por addAll ()?" Boa pergunta, acho que estava trabalhando com a suposição de que o hibernate teria menos probabilidade de ver isso como uma atualização do banco de dados se você não removesse os itens antes de adicioná-los novamente. Mas suspeito que não funcione dessa maneira.
Xpusostomos
2
Você deve usar reterAll () sobre limpar (), caso contrário, poderá eliminar as funções se passar o mesmo objeto definido. Por exemplo: user.setRoles (user.getRoles ()) == user.roles.clear ()
Martin
2
Quando você define orphanRemoval = true e cria um registro em que essa coleção é nula, você também recebe esse erro. Portanto: a tem oneToMany b com orphanremoval = true. Quando você cria A em que B = nulo, esse problema é acionado. Sua solução para inicializar e finalizá-la parece a melhor.
18716 Lawrence
Obrigado! Eu estava inicializando minha lista como List<String> list = new ArrayList<>();. Mudá-lo para List<String> list = null;corrigir o problema :)
Radical
19

Na verdade, meu problema era sobre iguais e hashcode das minhas entidades. Um código legado pode trazer muitos problemas, nunca se esqueça de conferir. Tudo o que fiz foi apenas manter a estratégia de excluir órfãos e corrigir os iguais e o código de hash.

axcdnt
fonte
9
por favor, um exemplo de métodos equals e hashCode bem feitos? porque tenho muitos problemas: ou não consigo atualizar meu conjunto ou recebo um erro do StackOverflow. Eu abri uma pergunta aqui: stackoverflow.com/questions/24737145/… . graças
SaganTheBest
11

Eu tive o mesmo erro. O problema para mim foi que, depois de salvar a entidade, a coleção mapeada ainda era nula e, ao tentar atualizar a entidade, a exceção foi lançada. O que me ajudou: Salvar a entidade, fazer uma atualização (a coleção não é mais nula) e executar a atualização. Talvez inicializar a coleção com o novo ArrayList () ou algo possa ajudar também.

Konsumierer
fonte
Enviar novo ArrayList em vez de nulo funcionou para mim. Obrigado
rpajaziti
4

TEM TIPO DE RELAÇÃO:


Não tente instanciar a coleção quando ela estiver declarada hasMany, basta adicionar e remover objetos.

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

TIPO DE RELAÇÃO DE USO:


Mas a coleção pode ser nula somente quando é declarada como uma propriedade (relação de uso) e não é inicializada na declaração.

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

Eu usei a abordagem @ user2709454 com pequenas melhorias.

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);
            }
        }
    }
}
luwojtaszek
fonte
3

Eu tive esse problema ao tentar usar TreeSet. Eu inicializei oneToManycom o TreeSetque funciona

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

Mas, isso trará o erro descrito questionacima. Parece que é hibernatesuportado SortedSete se alguém mudar a linha acima para

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

funciona como mágica :) mais informações hibernate SortedSetpodem estar aqui

carvalho
fonte
Talvez, é melhor para o usuário Collections.emptySet () para usar um singleton vazio lista
ryzhman
3

A única vez que recebo esse erro é quando tento passar NULL para o setter da coleção. Para evitar isso, meus setters ficam assim:

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

Eu me deparei com isso ao atualizar uma entidade com uma solicitação de postagem JSON. O erro ocorreu quando atualizei a entidade sem dados sobre os filhos, mesmo quando não havia nenhum. Adicionando

"children": [],

ao organismo da solicitação resolveu o problema.

cooperar
fonte
1

Uma outra causa pode estar usando o lombok.

@Builder- causas para salvar, Collections.emptyList()mesmo se você disser.myCollection(new ArrayList());

@Singular - ignora os padrões no nível da classe e deixa o campo null mesmo que o campo da classe tenha sido declarado comomyCollection = new ArrayList()

Meus 2 centavos, apenas passei 2 horas com o mesmo :)

Jan Zyka
fonte
1

Eu estava pegando A collection with cascade=”all-delete-orphan” was no longer referenced by the owning entity instancequando estava sentado parent.setChildren(new ArrayList<>()). Quando mudei paraparent.getChildren().clear() , ele resolveu o problema.

Verifique para obter mais detalhes: HibernateException - Uma coleção com cascade = "all-delete-orphan" não era mais referenciada pela instância da entidade proprietária .

Assim como
fonte
1

Estou usando o Spring Boot e tive esse problema com uma coleção, apesar de não a sobrescrever diretamente, porque estou declarando um campo extra para a mesma coleção com um serializador e desserializador personalizado , a fim de fornecer uma representação mais amigável ao front-end do dados:

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

Parece que mesmo que eu não estou substituindo a coleção mim mesmo , a desserialização faz isso sob o capô, provocando esta questão tudo a mesma coisa. A solução foi alterar o setter associado ao desserializador para limpar a lista e adicionar tudo, em vez de substituí-lo:

  @JsonDeserialize(using = AttributeDeserializer.class)
  public void setAttributesList(List<Attribute> attributes) {
    this.attributes.clear();
    this.attributes.addAll(attributes);
  }
Sofia Paixão
fonte
1

Eu tive o mesmo problema, mas foi quando o conjunto foi nulo. Somente na coleção Definir na lista de trabalho encontra. Você pode tentar a anotação de hibernação @LazyCollection (LazyCollectionOption.FALSE) instalada na anotação de JPA fetch = FetchType.EAGER.

Minha solução: esta é minha configuração e funciona bem

@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;
Dario Gaston
fonte
1
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>();

Ocorreu o mesmo erro ao adicionar o objeto filho à lista existente de objetos filho.

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

O que resolveu meu problema está mudando para:

child = childService.saveOrUpdate(child);

Agora a criança também revive com outros detalhes e funcionou bem.

Neha Gupta
fonte
0

Adicionando minha resposta idiota. Estamos usando o Spring Data Rest. Esse era o nosso relacionamento bastante padrão. O padrão foi usado em outro lugar.

//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

Com o relacionamento que criamos, sempre se pretendeu que as crianças fossem adicionadas através de seu próprio repositório. Eu ainda não tinha adicionado o repositório. O teste de integração que tínhamos estava passando por um ciclo de vida completo da entidade por meio de chamadas REST, para que as transações fossem fechadas entre solicitações. Nenhum repo para a criança significava que o json tinha os filhos como parte da estrutura principal em vez de dentro _embedded. Atualizações para o pai causariam problemas.

Snekse
fonte
0

A seguinte solução funcionou para mim

//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;
priyanka_rao
fonte
0

Em vez de atribuir nova coleção

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

Substitua todos os elementos por

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

tenha cuidado com

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

Este método também interrompe a hibernação.

Diógenes Ricardo
fonte
0

O meu era completamente diferente com o Spring Boot! Para mim, não foi devido à configuração da propriedade de coleção.

Nos meus testes, eu estava tentando criar uma entidade e estava recebendo esse erro para outra coleção que não era usada!

Depois de tanto tentar, acabei de adicionar um @Transactionalno método de teste e ele o resolveu. Não, não a razão embora.

madz
fonte
0

Isso contrasta com as respostas anteriores, eu tive exatamente o mesmo erro: "Uma coleção com cascade =” all-delete-órphan ”não era mais referenciada ...." quando minha função setter ficou assim:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

E então desapareceu quando eu mudei para a versão simples:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(versões de hibernação - tentei 5.4.10 e 4.3.11. Passei vários dias tentando todos os tipos de soluções antes de retornar à tarefa simples no setter. Confuso agora sobre o motivo disso.)

ligett
fonte