@OneToMany - Exclusão de filhos em cascata

Olá!!

Estou fazendo o mapeamento de uma lista de privilegios, 1 privilegio pode ter varios recurso, enquanto 1 recurso pode ter apenas 1 privilegio, quando eu passo da view para a aplicação ele grava no banco, mas quando eu altero ele faz o update da chave estrangeira para null e faz outro insert com os novos dados!!! Eu queria que ele excluisse do banco ao invés de gerar o update da chave estrangeira, dei uma olhada em dois posts feitos com o mesmo problema mais para a minha aplicaçao não resolveu!! Segue o código!!

Entity Recurso

@ManyToOne(cascade={CascadeType.ALL}, targetEntity=Privilegio.class) @JoinColumn(name="privilegio_cod") private Privilegio privilegio;

Entity Privilegio

@OneToMany(cascade={CascadeType.ALL}, targetEntity=Recurso.class, fetch=FetchType.EAGER, orphanRemoval=true) @JoinColumn(name="privilegio_cod") private Set<Recurso> recursos = new HashSet<Recurso>();

[quote=Mickdark]Olá!!

Estou fazendo o mapeamento de uma lista de privilegios, 1 privilegio pode ter varios recurso, enquanto 1 recurso pode ter apenas 1 privilegio, quando eu passo da view para a aplicação ele grava no banco, mas quando eu altero ele faz o update da chave estrangeira para null e faz outro insert com os novos dados!!! Eu queria que ele excluisse do banco ao invés de gerar o update da chave estrangeira, dei uma olhada em dois posts feitos com o mesmo problema mais para a minha aplicaçao não resolveu!! Segue o código!!

Entity Recurso

@ManyToOne(cascade={CascadeType.ALL}, targetEntity=Privilegio.class) @JoinColumn(name="privilegio_cod") private Privilegio privilegio;

Entity Privilegio

@OneToMany(cascade={CascadeType.ALL}, targetEntity=Recurso.class, fetch=FetchType.EAGER, orphanRemoval=true) @JoinColumn(name="privilegio_cod") private Set<Recurso> recursos = new HashSet<Recurso>();

[/quote]

Não sei se irá resolver seu problema mas tente tirar o cascade da entidade Recurso

Olá Thiago!

Tentei e não deu certo!! Tens mais alguma sugestão?

@ManyToOne(targetEntity=Privilegio.class) @JoinColumn(name="privilegio_cod")

Está usando qual provider? Imagino que isso deve ser configurável, não? Utilizo o Hibernate e nunca passei por esse tipo de situação

Então eu estou utilizando o Hibernate 3.6.8, no entando eu dei uma olhada nas documentações e em alguns foruns e percebi que pra usar um save ou update no hibernate para ele persistir eu tenho que implementar o equals e o HashCode!! Eu implementei o equals, agora o HashCode eu não sei como funciona, por isso estava procurando, e lendo uns tutos para aprender como ele funciona e como implementar esse método!!

Se estiver utilizando o Eclipse, ele gera para você. Acho que o netbeans também deve ter algo parecido.

no eclipse, acesse o menu source e generate hashCode() and equals() …

Segue um exemplo gerado no eclipse:

import java.io.Serializable;

public class User implements Serializable {

	private static final long serialVersionUID = -1033450779801862683L;

	private Long id;
	private String name;
	private String lastName;

	public User() {

	}

	public Long getId() {
		return this.id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getName() {
		return this.name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getLastName() {
		return this.lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result	+ ((lastName == null) ? 0 : lastName.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		if (lastName == null) {
			if (other.lastName != null)
				return false;
		} else if (!lastName.equals(other.lastName))
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
}

Ta usando hibernate?
Use a annotation no relacionamento:

[quote=marciobarroso]Se estiver utilizando o Eclipse, ele gera para você. Acho que o netbeans também deve ter algo parecido.

no eclipse, acesse o menu source e generate hashCode() and equals() …

Segue um exemplo gerado no eclipse:

[code]
import java.io.Serializable;

public class User implements Serializable {

private static final long serialVersionUID = -1033450779801862683L;

private Long id;
private String name;
private String lastName;

public User() {

}

public Long getId() {
	return this.id;
}

public void setId(Long id) {
	this.id = id;
}

public String getName() {
	return this.name;
}

public void setName(String name) {
	this.name = name;
}

public String getLastName() {
	return this.lastName;
}

public void setLastName(String lastName) {
	this.lastName = lastName;
}

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + ((id == null) ? 0 : id.hashCode());
	result = prime * result	+ ((lastName == null) ? 0 : lastName.hashCode());
	result = prime * result + ((name == null) ? 0 : name.hashCode());
	return result;
}

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	User other = (User) obj;
	if (id == null) {
		if (other.id != null)
			return false;
	} else if (!id.equals(other.id))
		return false;
	if (lastName == null) {
		if (other.lastName != null)
			return false;
	} else if (!lastName.equals(other.lastName))
		return false;
	if (name == null) {
		if (other.name != null)
			return false;
	} else if (!name.equals(other.name))
		return false;
	return true;
}

}
[/code][/quote]

Obrigado Marcio, mas quando eu gerei o código ele está dando uma exception do hibernate, “org.hibernate.LazyInitializationException”

[quote=Tchello] Ta usando hibernate?
Use a annotation no relacionamento:

Sim sim, eu estou tentando desta maneira depois que me responderam a primeira vez, mas ainda nada, olha o código como está neste momento!!!

[code]
@Entity
@Component
public class Privilegio implements Serializable {
private static final long serialVersionUID = -6175377460054211138L;
@Id
@GeneratedValue(generator = “Privilegio”)
@SequenceGenerator(name = “Privilegio”, initialValue = 20, sequenceName = “sequence_privilegio”)
private Long cod;
@Column(name=“Nome”)
private String nome;
@ManyToOne(cascade={CascadeType.ALL}, targetEntity=Empresa.class )
@ForeignKey(name=“CodEmpresa_Privilegio_FK”)
private Empresa empresa;

@OneToMany(targetEntity=Recurso.class, orphanRemoval=true)//, orphanRemoval=true
@Cascade(value={org.hibernate.annotations.CascadeType.ALL})
@JoinColumn(name="privilegio_cod")
private Set<Recurso> recursos = new HashSet<Recurso>();[/code]

Você precisa utilizar o FetchType para dizer ao hibernate que sua coleção deve ser inteiramente carregada logo de cara. Se você não configura isso, o hibernate só leva uma referencia dos itens da lista, tendo posteriormente que carregá-los quando for necessário.

Ai, surgem outros problemas … se você estiver enviando estes pojos para o jsp para renderização, você vai precisar implementar um filtro do tipo OpenSessionInView, para carregar a lista, ou então, carrega a lista antes de enviar para o jsp.

Boa sorte

[quote=marciobarroso]Você precisa utilizar o FetchType para dizer ao hibernate que sua coleção deve ser inteiramente carregada logo de cara. Se você não configura isso, o hibernate só leva uma referencia dos itens da lista, tendo posteriormente que carregá-los quando for necessário.

Ai, surgem outros problemas … se você estiver enviando estes pojos para o jsp para renderização, você vai precisar implementar um filtro do tipo OpenSessionInView, para carregar a lista, ou então, carrega a lista antes de enviar para o jsp.

Boa sorte[/quote]

É verdade, eu tinha esquecido de colocar o EAGER no fetch, bom, o problema, desse fetch foi resolvido de outra forma, quando o eclipse implementou o HashCode ele colocou a igualdade para todos os campos, mas no meu caso eu queria apenas do nome do privilegio e da empresa, então eu comentei os outros dois campos, e esse erro do FetchTypeLazyException não apareceu mais, agora vou testar se arrumando o HashCode da outra tabela ele exclui os valores depois de atualizar XD!!!

Como o Eclipse Implementou

@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((cod == null) ? 0 : cod.hashCode()); result = prime * result + ((empresa == null) ? 0 : empresa.hashCode()); result = prime * result + ((nome == null) ? 0 : nome.hashCode()); result = prime * result + ((recursos == null) ? 0 : recursos.hashCode()); return result; }

Como eu arrumei

@Override public int hashCode() { final int prime = 31; int result = 1; // result = prime * result + ((cod == null) ? 0 : cod.hashCode()); result = prime * result + ((empresa == null) ? 0 : empresa.hashCode()); result = prime * result + ((nome == null) ? 0 : nome.hashCode()); // result = prime * result + ((recursos == null) ? 0 : recursos.hashCode()); return result; }

Finalmente eu consegui XD!!!

Eu mudei alguns mapeamentos e mudei algumas coisas no equals e hashCode, no final das contas tenho quase certeza de que o problema estava realmente no mapeamento da entidade colocando Merge e alterando por merge também e não mais por saveOrUpdate, bom pra quem estiver passando por este problema segue meu código completo!! Ha esqueci de dizer, eu estou usando o VRaptor como MVC, por isso algumas annotations que não fazem parte da JPA, como @Component !!

Explicação: Tabela de Privilegios, 1 privilegio pode ter 0 ou mais recursos (OneToMany), e cada recurso tem que ter pelo menos 1 privilegio (ManyToOne)

[code]
@Entity
@Component
public class Recurso implements Serializable, Comparable{
private static final long serialVersionUID = 826924108132014590L;
@Id
@GeneratedValue(generator = “Recurso”)
@SequenceGenerator(name = “Recurso”, initialValue = 1, sequenceName = “sequence_recurso”)
private Long cod;
@Column
private String nome;
@Transient
private String descRecurso;
@ManyToOne
private Privilegio privilegio;

@Override
public int compareTo(Recurso recurso) {
	return this.descRecurso.compareTo(recurso.getDescRecurso());
}

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	
	if (!(cod==null)){
		return prime * result + ((cod == null) ? 0 : cod.hashCode());
	}

// result = prime * result + ((descRecurso == null) ? 0 : descRecurso.hashCode());
result = prime * result + ((nome == null) ? 0 : nome.hashCode());
// result = prime * result + ((privilegio == null) ? 0 : privilegio.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
	if (this == obj)
		return true;
	if (obj == null)
		return false;
	if (getClass() != obj.getClass())
		return false;
	Recurso other = (Recurso) obj;
	
	if(!(cod==null)){
		return cod.equals(other.getCod());
	}else if(!(nome==null)){
		return nome.equals(other.getNome());
	}
	return false;
}

Getters e Setters…[/code]

[code]
@Entity
@Component
public class Privilegio implements Serializable {
private static final long serialVersionUID = -6175377460054211138L;
@Id
@GeneratedValue(generator = “Privilegio”)
@SequenceGenerator(name = “Privilegio”, initialValue = 1, sequenceName = “sequence_privilegio”)
private Long cod;
@Column(name=“Nome”)
private String nome;
@ManyToOne(cascade={CascadeType.ALL}, targetEntity=Empresa.class, fetch=FetchType.EAGER )
@Cascade(value={
org.hibernate.annotations.CascadeType.SAVE_UPDATE,
org.hibernate.annotations.CascadeType.REMOVE})
@ForeignKey(name=“CodEmpresa_Privilegio_FK”)
private Empresa empresa;

@OneToMany(targetEntity=Recurso.class, fetch=FetchType.EAGER, orphanRemoval=true)//, orphanRemoval=true
@Cascade(value={
		org.hibernate.annotations.CascadeType.SAVE_UPDATE,
		org.hibernate.annotations.CascadeType.DELETE,
		org.hibernate.annotations.CascadeType.MERGE
		})
@JoinColumn(name="privilegio_cod")
private Set<Recurso> recursos = new HashSet<Recurso>();

@Override
public int hashCode() {
	final int prime = 31;
	int result = 1;
	
	result = prime * result + ((cod == null) ? 0 : cod.hashCode());
	if (cod==null){
		result = prime * result + ((empresa == null) ? 0 : empresa.hashCode());
		result = prime * result + ((nome == null) ? 0 : nome.hashCode());
	}

// result = prime * result;// + ((recursos == null) ? 0 : recursos.hashCode())
return result;
}

@Override
public boolean equals(Object obj) {
	if (this == obj) return true;
	if (obj == null) return false;
	if (getClass() != obj.getClass()) return false;
	Privilegio privilegio = (Privilegio) obj;
	
	if (!(cod==null)){
		return cod.equals(privilegio.getCod());
	}else if (!(empresa==null && nome==null)){
			if(!(privilegio.getEmpresa()==null && privilegio.getNome()==null)){
				return empresa.equals(privilegio.empresa) && nome.equals(privilegio.nome);
			}
	}
	return false;
}

Getters e Setters…[/code]

Código que faz o merge de acordo com a requisição na JSP!!

this.privDao.mergePrivilegio(privilegio);

Metodo criado no privilegioDao!!

public void mergePrivilegio(Privilegio privilegio) { operacoes.merge(privilegio); }

Eu fiz uma classe que guarda todas as consultas padrões, só não sei se é uma boa idéia, mas ta ai!!

public class HibernateOperacoes {
	private final Session session;
	private final Object obj;
	
	public HibernateOperacoes(Object obj, Session sessao) throws Throwable {
		System.out.println("Entrou no Construtor do Hibernate");
		this.obj = obj;
		this.session = sessao;
	}

	public void merge(Object obj) {
		session.beginTransaction();
		session.merge(obj);
		session.getTransaction().commit();
	}
...
}

Agora só preciso ver o de exclusão em cascata !!!

O cascateamento utilizando Delete não funcionou de jeito nenhum, gostaria de saber se alguem ja tentou fazer um delete em um objeto desanexado apenas com o id?

Eu fiz um teste com JUnit, assim!!

[code]
//Cria um recurso para salvar no objeto privilegio
Recurso recurso = new Recurso();
recurso.setNome(“Teste_Exclusao (Rec)”);

	//Cria o Objeto privilegio para ser salvo
	Privilegio privilegio = new Privilegio();
	privilegio.setNome("Teste_Exclusao (Priv)");
	privilegio.getRecursos().add(recurso);
	
	//Salva o Objeto privilegio
	sessao.beginTransaction();
	sessao.save(privilegio);
	sessao.getTransaction().commit();
	sessao.close();
	
	//Cria um novo privilegio so com o codigo settado para testar a exclusao
	Privilegio privilegio2 = new Privilegio();
	privilegio2.setCod(privilegio.getCod());
	
	//Exclui o segundo privilegio para ver se ele tambem exclui os recursos
	sessao = factory.openSession();
	sessao.beginTransaction();
	System.out.println("-------Vai Deletar-------");

// privilegio2 = (Privilegio) sessao.load(Privilegio.class, privilegio.getCod());
sessao.delete(privilegio2);

	System.out.println("---------Deletou---------");
	sessao.getTransaction().commit();
	
	//Pega a lista de recursos sem o privilegio na FK
	List<Recurso> recursos = (List<Recurso>) sessao.createQuery("from Recurso where privilegio_cod is null").list();
	
	sessao.close();
	
	//Verifica se a lista de recursos possui algumsa coisa, se possuir esta errado, porque significa que ele
	//nao excluiu a lista de recursos no banco
	assertEquals(0, recursos.size());[/code]

A unica maneira de fazer com que ele realizasse a exclusão em cascata, “Alem de ter o @Cascade(value={CascateType.DELETE}”, foi chamando um refresh antes de excluir o objeto só com o id setado!!

Alguem sabe me dizer se desta maneira está correta?
//Exclui objeto pai e os objetos contidos na lista do HashSet!!
session.beginTransaction();
session.refresh(obj);
session.delete(obj)
session.getTransaction().commit();
session.close();