[RESOLVIDO] Hibernate - CascadeALL não funciona alterando listas

9 respostas
B

PessoALL

Estou fazendo uns testes com o Hibernate e estou passando por um problema estranho. Tenho duas classes, Contato e ContatoEndereco, relacionados num Onetomany com cascadeall. Vejam abaixo as definições dessas classes:

@Entity
@Table(name = "contatos")
public class Contato {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;
    
    @Column(name = "nome", nullable = false, length = 100)
    private String nome;
    
    @OneToMany(cascade = CascadeType.ALL,mappedBy="objContato")
    private List<ContatoEndereco> listaEnderecos;

    public Contato() {
    }

    // getters e setters removidos
    
}

@Entity
@Table(name = "contatos_enderecos")
public class ContatoEndereco {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;
    
    @Column(name = "nomeLogradouro", nullable = false, length = 100)
    private String nomeLogradouro;
    
    @Column(name = "numero", length = 20)
    private String numero;
    
    @ManyToOne
    @JoinColumn(name="id_contato")
    private Contato objContato;

    public ContatoEndereco() {
    }

    // getters e setters removidos
        
}

No teste de cadastrar o objeto, estou instanciando o contato e populando sua lista com tres contatoEndereco. Isso posto, bastou um persist no objeto Contato e a lista de seus endereços foi corretamente cadastrada no banco. Funciona 100%, 10x cascadeall. Vamos ao “show me the code”:

public void inicializaTeste() {

        em = Persistence.createEntityManagerFactory("testeAgendaPU").createEntityManager();

        instanceContatoJpaController = new ContatoJpaController();


    }

    @Test
    public void testCadastraContatoComTresEnderecos_ComSucesso() {
	
        inicializaTeste();

        limpaArquivoDeContatos();

        EntityTransaction transacao = em.getTransaction();
        transacao.begin();

	// INSTANCIA O CONTATO
        Contato contato = new Contato();

        contato.setNome("CONTATO");
        contato.setFoneConvencional("[telefone removido]");

	// INSTANCIA A LISTA DE ENDERECOS DO CONTATO
        List<ContatoEndereco> listaEnderecos = new ArrayList();

        ContatoEndereco obj1 = new ContatoEndereco("RUA", "GENERAL CAMARA", "PORTO ALEGRE", "RS");
        obj1.setObjContato(contato);

        listaEnderecos.add(obj1);

        ContatoEndereco obj2 = new ContatoEndereco("AVENIDA", "PROTASIO ALVES", "PORTO ALEGRE", "RS");
        obj2.setObjContato(contato);

        listaEnderecos.add(obj2);

        ContatoEndereco obj3 = new ContatoEndereco("TRAVESSA", "ANGUSTURA", "PORTO ALEGRE", "RS");
        obj3.setObjContato(contato);

        listaEnderecos.add(obj3);

        contato.setContatoEnderecoList(listaEnderecos);

	// PERSISTE TUDO NUM SO COMANDO
        em.persist(contato);

        transacao.commit();

    }

No teste de remover o objeto Contato, basta um remove e tudo, inclusive os endereços, é deletado da base. Beleza pura, tudo certo.

public void testRemocaoDeClienteComTresEnderecos() throws IllegalOrphanException, NonexistentEntityException {

        inicializaTeste();

        EntityTransaction transacao = em.getTransaction();
        
        transacao.begin();

        Long idEmQuestao = instanceContatoJpaController.buscaUltimoId(em);
        
        Contato contato = instanceContatoJpaController.findContato(em, idEmQuestao);
        
        Assert.assertNotNull("Não foi localizado um contato com id="+idEmQuestao, contato);
        Assert.assertEquals("A lista do contato deveria ter sido retornada com TRES itens.", 3, contato.getContatoEnderecoList().size());

	// REMOVE O OBJETO COM A SUA LISTA DE ENDERECOS
	em.remove(contato);        

	// VERIFICA SE AINDA EXISTE O REGISTRO
        Contato contatoNovo = instanceContatoJpaController.findContato(em, idEmQuestao);

        Assert.assertNull("Parece que o contato com id="+idEmQuestao+" não foi removido da base porque eu ainda o encontrei", contatoNovo);
        
        transacao.commit();

    }

Mas meu problema é na modificacao da lista. Recupero o objeto com os três enderecos (verificando se vieram tres elementos), deleto o primeiro elemento da lista e persisto o objeto Contato. Tu indicaria que o elemento seria excluido da base de dados. Mas não. Esse item da lista NÃO É EXCLUIDO. Inclusive, no teste, faço nova recuparacao do objeto e checo se a partir daquele momento a lista tem dois e apenas dois itens e, no teste isso passa. Tipo, parece que deu certo até que eu vou olhar no banco de dados e constato que o registro ainda está lá…

public void testRemoveUmEnderecoDeUmClienteComTresEnderecos() {

        EntityTransaction transacao = em.getTransaction();

        transacao.begin();

	// DESCOBRE O ULTIMO ID DE CONTATO CADASTRADO
        Long idEmQuestao = instanceContatoJpaController.buscaUltimoId(em);

	// VAI  E TRAZ O OBJETO REFERENTE ÀQUELE ID	
        Contato contato = instanceContatoJpaController.findContato(em, idEmQuestao);

	// CONFERE SE A LISTA DE ENDERECOS ESTÁ MESMO COM TRÊS ITENS
        Assert.assertEquals("A lista deveria ter sido retornada com TRES itens.", 3, contato.getContatoEnderecoList().size());

	// PEGA O PRIMEIRO OBJETO ENDERECO DA LISTA
        ContatoEndereco objContatoEnderecoDeletado= (ContatoEndereco) contato.getContatoEnderecoList().remove(0);
        
	// REMOVE O BICHO DA LISTA DE ENDERECOS DO CONTATO
        contato.getContatoEnderecoList().remove(objContatoEnderecoDeletado);
        
	//  UMA ALTERADA NO NOME DO CLIENTE,  PRA VER SE, PELO MENOS ISSO, REFLETE NO BD
        contato.setNome("CONTATO ALTERADO");
        
	//  UM MERGE  NO BO
        instanceContatoJpaController.edit(em, contato);
        
	// PASSA A RÉGUA E FECHA A CONTA (OU SEJA, COMMITA O TREM)
        transacao.commit();

	// PRA DESENCARGO DE CONSCIENCIA, BUSCA DE NOVO O OBJETO
        Contato novoContato = instanceContatoJpaController.findContato(em, idEmQuestao);

	// VERIFICA SE A LISTA AGORA TEM APENAS DOIS ITENS, AO INVÉS DE TRÊS
        Assert.assertEquals("A lista deveria ter sido retornada com DOIS itens.", 2, novoContato.getContatoEnderecoList().size());

    }

O que posso estar fazendo errado? Ou, de repente, não ter compreendido corretamente? O cascadeALL não deveria ter deletado mesmo o item?

Agradeço a qualquer boa alma que possa me ajudar nesse esclarecimento.

Abraço a todos

9 Respostas

V

Adicione a tag orphanRemovel=true

Isso acontece porque ao excluir um objeto da sua lista, ele fica"orfão", ou seja, existe um endereço sem um contato. Por isso dessa opção!
Creio que resolverá o seu problema!

B

Não tenho esse parametro no @onetomany, Vagner.

Mas com tua dica e, fui pesquisar e descobri um tal de @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) e fiquei cheio de esperança.

Minha declaração ficou, então, assim:

@OneToMany(cascade = CascadeType.ALL,mappedBy="objContato")
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    private List<ContatoEndereco> listaEnderecos;

Mas não funcionou mesmo assim. Continua não deletando o item da lista na base.

Em todo caso, obrigado pela tentativa.

Abrassssssssss

V

Pra funcionar você vai ter que excluir o objeto da lista e depois dar um merge ou save na entidade contato!

A opção orphan-remove só aparece na jpa2. Baixe ele e coloque no seu projeto. Minha recomendação é para que você atualize as libs do projeto do hibernate. Por exemplo, se estiver usando hibernate 3, pegue o ultimo release. Você vai perceber que vai dar alguns conflitos de bibliotecas, mas você encontrará no google como solucioná-los.

Bom, você já tem o caminho das pedras, agora é só ver porque o orphan-removal não está funcionando!

R

Eu trabalho com Hibernate, mas com session, e sempre que vou atualizar algo eu do session.update(objeto), depois uso transaction.commit().

No seu caso,

PEGA O PRIMEIRO OBJETO ENDERECO DA LISTA  
    ContatoEndereco objContatoEnderecoDeletado= (ContatoEndereco) contato.getContatoEnderecoList().remove(0);  
      
REMOVE O BICHO DA LISTA DE ENDERECOS DO CONTATO  
    contato.getContatoEnderecoList().remove(objContatoEnderecoDeletado);

Acho que voce deveria nao pegar o ContatoEndereco ja removendo o objeto, pois voce praticamente da 2 remove.

Acho melhor voce pegar todos os enderecos da lista, e depois contato.getEnderecoList().remove(contato.getEnderecoList().get(0))

Mais ou menos assim.

A

bonissauro:
Não tenho esse parametro no @onetomany, Vagner.

Mas com tua dica e, fui pesquisar e descobri um tal de @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) e fiquei cheio de esperança.

Minha declaração ficou, então, assim:

@OneToMany(cascade = CascadeType.ALL,mappedBy="objContato")
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    private List<ContatoEndereco> listaEnderecos;

Mas não funcionou mesmo assim. Continua não deletando o item da lista na base.

Em todo caso, obrigado pela tentativa.

Abrassssssssss

ola, este mappedBy não deveria estar do outro lado do relacionamento ?

B

VagnerMG:
Pra funcionar você vai ter que excluir o objeto da lista e depois dar um merge ou save na entidade contato!

A opção orphan-remove só aparece na jpa2. Baixe ele e coloque no seu projeto. Minha recomendação é para que você atualize as libs do projeto do hibernate. Por exemplo, se estiver usando hibernate 3, pegue o ultimo release. Você vai perceber que vai dar alguns conflitos de bibliotecas, mas você encontrará no google como solucioná-los.

Bom, você já tem o caminho das pedras, agora é só ver porque o orphan-removal não está funcionando!

Grande, Vagner. Quando falaste em jpa 2.0 me ligou no cerebro uma luzinha amarela. Fui até as propriedades do projeto e vi que tinha lá uns jar meio esquisitos (tipo, me pareceram redundantes): Hibernate, Persistencia e Eclipse Top-link JPA 2. Então deletei o Hibernate e o Persistence. Quando apliquei essas modificações, ele me deu uma xingada sobre as namedqueries que o netbeans havia criado. Como não eram meu problema atual, deletei essas namedqueries da entidade Contato e percebi que o tal parametro orphan-remove apareceu na annotation. Ao rodar meu teste, “voilá” !!! Funcionou que foi uma belezura !!! Agora, vou descobrir a sistemática das named queries no jpa 2, mas isso é oooooutra história.

Abração e muito obrigado

B

rof20004,

na verdade era isso mesmo que eu estava tentando fazer. Aquele primeiro remove era pra ser um get. Mesmo acertando esse detalhe, não resolveu o problema. Depois da dica do Vagner, mudei as bibliotecas e, usando a jpa 2, a coisa andou.

Em todo caso, agradeço a tentativa de me ajudar.

Abrassssssss

B

aix,

no outro lado do relacionamento, classe contatoendereco, eu preciso ter um manytoone apontando para o contato, mas essa anotação nao possui o parametro mappedby.

Em todo caso, agradeço a tentativa de me ajudar.

Abrassssssss

A

bonissauro:
aix,

no outro lado do relacionamento, classe contatoendereco, eu preciso ter um manytoone apontando para o contato, mas essa anotação nao possui o parametro mappedby.

Em todo caso, agradeço a tentativa de me ajudar.

Abrassssssss

de nada, o importe é resolver :wink:

Criado 15 de novembro de 2012
Ultima resposta 17 de nov. de 2012
Respostas 9
Participantes 4