Many to Many - org.hibernate.PersistentObjectException: detached entity passed to persist:

18 respostas
d34d_d3v1l

Galera, estou tentando salvar uma entidade com relacionamento ManyToMany com atributos…
Estou recebendo esta bendita exception…

Caused by: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: pacote.Campo at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1360) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1288) at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1294) at org.hibernate.ejb.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:877) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240) at $Proxy31.merge(Unknown Source) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:345) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:334) at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:319) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:155) ... 61 more

Vamos aos mapeamentos…

Entidade que quero salvar:

@Table(name = "com_prospecto")
@Entity
public class Prospecto {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column(name = "data_cadastramento")
	@Temporal(TemporalType.TIMESTAMP)
	private Date cadastramento;

	@ManyToMany
	@JoinTable(name = "com_historico_acoes", joinColumns = @JoinColumn(name = "id_prospecto"), inverseJoinColumns = @JoinColumn(name = "id_acao"))
	private List<Acao> acoes;

	@OneToMany(fetch = FetchType.EAGER, mappedBy = "prospecto")
	private List<ProspectoCampo> prospectoCampos;

Entidade que quero que também seja salva:

@Table(name = "com_prospecto_campo")
@Entity
@IdClass(ProspectoCampoId.class)
public class ProspectoCampo {

	@Id
	@ManyToOne
	@JoinColumn(name = "id_prospecto")
	private Prospecto prospecto;

	@Id
	@ManyToOne
	@JoinColumn(name = "id_campo")
	private Campo campo;

	private String valor;

Id da classe anterior:

public class ProspectoCampoId implements Serializable {

	private static final long serialVersionUID = 1L;

	private Integer prospecto;

	private Integer campo;

OBS: este mapeamento foi produto da leitura do seguinte blog do jakefrog (H. Coelho)!

E estou tentando salvar (na verdade é editar, mas isto é indiferente pois o Spring Data faz um save or update):

public String editar(){
		prospecto = prospectoService.buscarProspectoComAcoes(prospecto.getId()); //isto é por causa do lazy loading...
		
		for(ProspectoHelper hp : ProspectoHelper.extrairDadoDoModelo(dynaFormModelUniao)){ //ignorem esta linha
			prospecto.adicionarCampo(hp.getPropertyFilter().getCampo(), hp.getPropertyFilter().getValue());   //criei um metodo no modelo para adicionar o campo... ja vou mostrar
		}
		
		prospecto.adicionarAcao(acaoSelecionada);
		
		try{
			prospectoService.save(prospecto);
		}catch(Exception e){
			log.error("Erro ao editar Prospecto!", e);		
			messageUtil.sendErrorMessageToUser("global.error", "global.insert.error", e.getMessage());
			return null;
		}
		
		return getRadarPath();
	}

O método que criei no modelo:

public void adicionarCampo(Campo campo, Object value) {
		ProspectoCampo encontrado = encontrarProspectoCampo(campo);
		
		if(encontrado == null){
			ProspectoCampo pc = new ProspectoCampo();
                        pc.setProspecto(this);
			pc.setCampo(campo);
			pc.setValor(value.toString());
		}else{
			encontrado.setValor(value.toString());			
		}
	}

18 Respostas

Rafael_Guerreiro

Tente adicionar o cascade = CascadeType.ALL:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "prospecto", cascade = CascadeType.ALL) private List&lt;ProspectoCampo&gt; prospectoCampos;

d34d_d3v1l

Rafael Guerreiro:
Tente adicionar o cascade = CascadeType.ALL:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "prospecto", cascade = CascadeType.ALL) private List&lt;ProspectoCampo&gt; prospectoCampos;

Ja tentei fazer isso…
Mas li num post que o Lucas Cavalcanti disse que o cascade não é aplicacável a variaveis que são ‘mappedBy’…
Mas vou adicionar, dar clean em tudo e testar… só pra garantir!!
vlwww

**EDIT

continua com o mesmo erro :frowning:

K

tenta fazer uma consulta a esse objeto antes de seta-lo em outro para então mandar
salvar.

Rafael_Guerreiro

Não! Eu falei besteira… :oops:

Se fosse pra fazer, seria aqui…

@Id @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name = "id_campo") private Campo campo;
Tenha certeza de que o CAMPO que você está adicionando já está salvo no banco.

d34d_d3v1l

Rafael Guerreiro:
Não! Eu falei besteira… :oops:

Se fosse pra fazer, seria aqui…

@Id @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name = "id_campo") private Campo campo;
Tenha certeza de que o CAMPO que você está adicionando já está salvo no banco.

Infelizmente,
mesmo erro :frowning:

K

d34d_d3v1l:
Rafael Guerreiro:
Não! Eu falei besteira… :oops:

Se fosse pra fazer, seria aqui…

@Id @ManyToOne(cascade=CascadeType.ALL) @JoinColumn(name = "id_campo") private Campo campo;
Tenha certeza de que o CAMPO que você está adicionando já está salvo no banco.

Infelizmente,
mesmo erro :(

Tentou fazer uma consulta a esse objeto e setá-lo novamente
neste objeto que vc está tentando salvar?

d34d_d3v1l

kleberdamasco:

Tentou fazer uma consulta a esse objeto e setá-lo novamente
neste objeto que vc está tentando salvar?

Ok, vou fazer isso que vc falou…
Mas não vejo mto sentido, porque esta EAGER o fetchType…
Mas vou testar e já posto o resultado

K

d34d_d3v1l:
kleberdamasco:

Tentou fazer uma consulta a esse objeto e setá-lo novamente
neste objeto que vc está tentando salvar?

Ok, vou fazer isso que vc falou…
Mas não vejo mto sentido, porque esta EAGER o fetchType…
Mas vou testar e já posto o resultado

Pelo que sei, quando você faz uma consulta a um objeto o estado do objeto
está como transient, qndo é encerrada a sessão daquela consulta
é alterado o estado para detached por isso não é possível salvá-lo novamente.

Rafael_Guerreiro

Detached é um objeto que tem ID, mas não está sendo gerenciado pelo Hibernate/JPA.

Precisa fazer um merge para ele passar para “managed”… Ou fazer um get, caso você não queira mudar seus valores.

d34d_d3v1l

Kleber, quer que eu execute uma consulta pra buscar os Campos, que nem eu fiz com a ação? Tipo:

@Query("select p from Prospecto p left join fetch p.acoes where p.id = ?1")
	public Prospecto buscarProspectoComAcoes(Integer idProspecto);

Como eu faço aqui:

prospecto = prospectoService.buscarProspectoComAcoes(prospecto.getId());

Não é isso?

Bom , não consigo buscar 2 Lists com a mesma consulta, pq da aquele erro do hibernate, fetch multiple bags…
Algo assim…

Então o que sugerem?
Ou entendi errado?

abraços

K

Rafael Guerreiro:
Detached é um objeto que tem ID, mas não está sendo gerenciado pelo Hibernate/JPA.

Precisa fazer um merge para ele passar para “managed”… Ou fazer um get, caso você não queira mudar seus valores.

fazer um merge, caso tenha sido alterado, ou uma consulta para pegá-lo novamente.

Rafael_Guerreiro
Se isso aqui é por causa do Lazy Loading... Um Hibernate.initialize(prospecto.getList()); resolve.

Se isso aqui é por causa do Lazy Loading… Um Hibernate.initialize(prospecto.getList()); resolve.

d34d_d3v1l

ainda nao entendi…

tenho que buscar a coleção de campos… setar no objeto… adicionar os que eu quero
e depois salvar o prospecto… é isso?

Rafael_Guerreiro

Mas “Campo” em “ProspectoCampo” não é coleção.

você tem que fazer um Hibernate.initialize(prospecto.getProspectoCampos());

d34d_d3v1l

Rafael Guerreiro:
Mas “Campo” em “ProspectoCampo” não é coleção.

você tem que fazer um Hibernate.initialize(prospecto.getProspectoCampos());

ProspectoCampos já esta inicializado.

Acho que o problema é que ele esta tentando salvar as mudanças na entidade Campo também.
Mas não quero que faça isso!

Rafael_Guerreiro

Então, nesse caso, você não deve usar o Cascade.

Você vai precisar fazer um get (ou load) e setar com o objeto que veio do Hibernate.

d34d_d3v1l

Rafael Guerreiro:
Então, nesse caso, você não deve usar o Cascade.

Você vai precisar fazer um get (ou load) e setar com o objeto que veio do Hibernate.

Então, remove is cascade…

e fiz assim:

public String editar(){
		Prospecto prospecto = ProspectoHelper.extrairProspecto(dynaFormModelUniao);
		prospecto = prospectoService.buscarProspectoComAcoes(prospecto.getId());
		
		
		for(ProspectoHelper hp : ProspectoHelper.extrairDadoDoModelo(dynaFormModelUniao)){
			prospecto.adicionarCampo(campoService.findOne(hp.getPropertyFilter().getCampo().getId()), hp.getPropertyFilter().getValue());
		}
		
		prospecto.adicionarAcao(acaoSelecionada);
		
		try{
			prospectoService.save(prospecto);
		}catch(Exception e){
			log.error("Erro ao editar Prospecto!", e);		
			messageUtil.sendErrorMessageToUser("global.error", "global.insert.error", e.getMessage());
			return null;
		}
		
		return getRadarPath();
	}

Mas mesmo assim deu erro!!! Será que eu não posso construir o objeto ProspectoCampo dentro da propria entidade? Não faz sentido né…

d34d_d3v1l

Galera,

tentei fazer assim:

public String editar(){
		Prospecto prospecto = ProspectoHelper.extrairProspecto(dynaFormModelUniao);
		prospecto = prospectoService.buscarProspectoComAcoes(prospecto.getId());
		
		log.debug("Antes: "+prospecto.getProspectoCampos().size());
		
		List<ProspectoCampo> prospectoCampos = new ArrayList<ProspectoCampo>();
		
		for(ProspectoHelper hp : ProspectoHelper.extrairDadoDoModelo(dynaFormModelUniao)){
			ProspectoCampo pc = new ProspectoCampo();
			pc.setCampo(campoService.findOne(hp.getPropertyFilter().getCampo().getId()));
			pc.setValor(hp.getPropertyFilter().getValue().toString());
			pc.setProspecto(prospecto);
			prospectoCampos.add(pc);
		}
		
		
		prospecto.setProspectoCampos(prospectoCampos);

		log.debug("Depois: "+prospecto.getProspectoCampos().size());
		
		prospecto.adicionarAcao(acaoSelecionada);
		
		try{
			prospectoService.save(prospecto);
		}catch(Exception e){
			log.error("Erro ao editar Prospecto!", e);		
			messageUtil.sendErrorMessageToUser("global.error", "global.insert.error", e.getMessage());
			return null;
		}
		
		return getRadarPath();
	}

Mas infelizmente mesmo erro!!!
LOG:

13:59:09 (ProspectoRadarController.java:150) S-DEBUG - Antes: 3

--mensagens do hibernate

13:59:09 (ProspectoRadarController.java:165) S-DEBUG - Depois: 4

--mais mensagens do hibernate

13:59:09 (ProspectoRadarController.java:172) S-ERROR - Erro ao editar Prospecto!
org.springframework.orm.jpa.JpaSystemException: org.hibernate.PersistentObjectException: detached entity passed to persist:
Criado 17 de dezembro de 2012
Ultima resposta 17 de dez. de 2012
Respostas 18
Participantes 3