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!


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

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

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.


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.


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.


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 = []
}

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

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


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

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.


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 :)



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

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;

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.


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.


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;

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

0

tenha cuidado com

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

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



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.


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.)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.