Problema atualização @OneToMany @ManyToOne

Salve Gujeiros!

Seguinte:

Tenho duas classes: Pessoa e PessoaEmail

Pessoa

[code]@Entity
@NamedQueries({
@NamedQuery(name=“getClientes”, query=“from Pessoa order by idPessoa”),
)
@Table(name = “pessoa”, schema = “public”)
@SequenceGenerator(name=“PESSOA_SEQ”, sequenceName=“pessoa_id_pessoa_seq”, allocationSize=1)
public class Pessoa implements java.io.Serializable {

@UIHiddenField(column = 10, line = 0)
@Id
@Column(name = "id_pessoa", unique = true, nullable = false, length = 14)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PESSOA_SEQ")
public Integer getIdPessoa() {
	return this.idPessoa;
}

public void setIdPessoa(Integer idPessoa) {
	this.idPessoa = idPessoa;
}

@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "cliente")
public Cliente getCliente() {
	return this.cliente;
}

public void setCliente(Cliente cliente) {
	this.cliente = cliente;
}


@OneToMany(fetch = FetchType.EAGER, mappedBy = "pessoa", cascade = CascadeType.ALL)
public List<PessoaEmail> getPessoaEmails() {
	return this.pessoaEmails;
}

public void setPessoaEmails(List<PessoaEmail> pessoaEmails) {
	this.pessoaEmails = pessoaEmails;
}

}[/code]

PessoaEmail

[code]@Entity
@Table(name = “pessoa_email”, schema = “public”, uniqueConstraints = @UniqueConstraint(columnNames = “email”))
@SequenceGenerator(name=“PESSOA_EMAIL_SEQ”, sequenceName=“pessoa_email_id_pessoa_email_seq”, allocationSize=1)
public class PessoaEmail implements java.io.Serializable {

@Id
@Column(name = "id_pessoa_email", unique = true, nullable = false)
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="PESSOA_EMAIL_SEQ")
public int getIdPessoaEmail() {
	return this.idPessoaEmail;
}

public void setIdPessoaEmail(int idPessoaEmail) {
	this.idPessoaEmail = idPessoaEmail;
}

@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "pessoa", nullable = false)
public Pessoa getPessoa() {
	return this.pessoa;
}

public void setPessoa(Pessoa pessoa) {
	this.pessoa = pessoa;
}

}
[/code]

Tenho o seguinte método que lê os dados de um txt e salva a entidade Pessoa e a entidade PessoaEmail.

[code]public Boolean importaEmails(String arquivoFonte) {
PessoaRepository pr = BeanFactory.getPessoaRepository(em);
while ((registro = readRegistro()) != null) {
boolean atualizar = false;

            // busca pessoa no banco
	Pessoa p = pr.getPessoaFisicaJuridica(registro[1]);
	List<PessoaEmail> pessoaEmail = null;

            // busca List<PessoaEmail> do relacionamento @OneToMany
	pessoaEmail = p.getPessoaEmails();
	if (pessoaEmail != null && pessoaEmail.size() > 0){
		temEmailCadastrado = true;
	}
			
	if (temEmailCadastrado) {
		atualizar = true;
	}


	if (!atualizar) {
		PessoaEmail pm = new PessoaEmail();
		pm.setNome("nfe");
		pm.setEmail(registro[2]);
		pm.setPessoa(p);

		if (salva(pm)) {
			logger.debug("Email importado: " + registro[1]);
		} else {
			logger.debug("Erro ao gravar Pessoa: " + registro[1]);
		}
	} else {
		PessoaEmail pm = pessoaEmail.get(0);
		pm.setEmail(registro[2]);
		
		em.getTransaction().begin();
		PessoaEmail pessoaEmailRet = em.merge(pm);
		em.getTransaction().commit();

		if (pessoaEmailRet != null) {
			logger.debug("Email atualizado: " + registro[2]);
		} else {
			logger.debug("Erro ao atualizar PessoaEmail: " + registro[1]);
		}
	}
}

fecha();

return Boolean.TRUE;

}

  public boolean salva(Object arg){
	try{
		tx.begin();
		em.persist(arg);
		tx.commit();
	}
	catch (Exception e){}
 }

[/code]

O problema é que durante a importação, o método p.getPessoaEmails(); não retorna a List.

Mas se após a importação eu chamar o método novamente, ele retorna a lista normalmente.

Isto seria um problema do hibernate?
Do EntityManager?

Já fiz alterações de EAGER e LAZY nos relacionamentos porém não obtive sucesso.

Se alguém puder ajudar, agradeço.

Abraços.

Olá!

Seu objeto precisa estar em estado gerenciável (não observei isso no código).
Para isso, utilize algo assim (em é sua instância do EntityManager):

p = em.merge(p);

Isso fará que seu objeto passe a ser gerenciável. Quando chamar o p.getPessoaEmails() o SELECT será executado e ele retornará a lista que você precisa.
Se isso não funcionar, pode ser algum problema de mapeamento.

Abraços!!

[quote=alevi]Olá!

Seu objeto precisa estar em estado gerenciável (não observei isso no código).
Para isso, utilize algo assim (em é sua instância do EntityManager):

p = em.merge(p);

Isso fará que seu objeto passe a ser gerenciável. Quando chamar o p.getPessoaEmails() o SELECT será executado e ele retornará a lista que você precisa.
Se isso não funcionar, pode ser algum problema de mapeamento.

Abraços!!

[/quote]

Obrigado pela ajuda alevi.

Fiz a seguinte alteração no método:

public Boolean importaEmails(String arquivoFonte) {
	PessoaRepository pr = BeanFactory.getPessoaRepository(em);
	while ((registro = readRegistro()) != null) {
		boolean atualizar = false;
			
                // busca pessoa no banco
		Pessoa p = pr.getPessoaFisicaJuridica(registro[1]);
		List<PessoaEmail> pessoaEmail = null;

                // busca List<PessoaEmail> do relacionamento @OneToMany
		p = em.merge(p);
        pessoaEmail = p.getPessoaEmails();
		if (pessoaEmail != null && pessoaEmail.size() > 0){
			temEmailCadastrado = true;
		}
.
.
.

Porém ele continua não retornando a lista de emails. :?

Seria essa a idéia mesmo?

Se alguém tiver mais alguma sugestão.

Se encontrar uma solução volto aqui pra postar. :wink:

[]'s

Veja se o select correto está sendo executado.

Estou executando esta query e ela está retornando o objeto Pessoa corretamente:

			Query q = em.createQuery("from Pessoa where cnpj = '" + busca.toLowerCase() + "' OR cpf = '" + busca.toLowerCase() + "' order by nome");

			pessoaBD = (List<Pessoa>) q.setMaxResults(10).getResultList();
		
			if (pessoaBD != null && pessoaBD.size() > 0)
				return pessoaBD.get(0);	

keyboarder, nos logs o hibernate imprime a query executada no formato SQL. Veja como habilitar os logs e veja se ele realmente executa a query…

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-logging

Abraços

[quote=garcia-jj]keyboarder, nos logs o hibernate imprime a query executada no formato SQL. Veja como habilitar os logs e veja se ele realmente executa a query…

http://docs.jboss.org/hibernate/core/3.3/reference/en/html/session-configuration.html#configuration-logging

Abraços[/quote]

Valeu pela dica garcia-jj.

Imprimi as consultas no log e parece que ele faz a busca corretamente. O problema é que a consulta não retorna o campo PessoaEmail. Acredito que o problema seja outro mesmo.

[]'s

Bom dia gujeiros!

Fiz a seguinte alteração no método de importação, e agora está funcionando.

Ao invés de chamar o método merge§ antes de fazer a busca, fiz a chamada do método em.refresh§. Agora o método p.getPessoaEmails() retorna os emails corretamente.

[code]public Boolean importaEmails(String arquivoFonte) {
PessoaRepository pr = BeanFactory.getPessoaRepository(em);
while ((registro = readRegistro()) != null) {
boolean atualizar = false;

            // busca pessoa no banco
	Pessoa p = pr.getPessoaFisicaJuridica(registro[1]);
	List<PessoaEmail> pessoaEmail = null;

            // busca List<PessoaEmail> do relacionamento @OneToMany
	em.refresh(p);
    pessoaEmail = p.getPessoaEmails();
	if (pessoaEmail != null && pessoaEmail.size() > 0){
		temEmailCadastrado = true;
	}

.
.
.[/code]

Essa alteração é válida?
Isso seria uma “gambiarra”? hehe

Edit

Pessoal, achei este link que explica sobre cache de entidades que é utilizado no TopLink.
http://weblogs.java.net/blog/guruwons/archive/2006/09/understanding_t_1.html

Este cache tb existe no Hibernate?

[]'s

Sim, aqui tem um post que citei sobre caches. No link você pode entender sobre eles: http://www.guj.com.br/posts/list/140145.java#754723.

Cache de primeiro nivel é sua propria sessão, ou seja, se você tem um objeto na sua session e por acaso você fizer uma busca ele nem chega a ir no database, e pega a instancia de sua session.

Segundo nivel são usando libs externas (link que te passei) usando diversas libs.

O refresh que você faz apenas torna os objetos “gerenciaveis pela session”. Só analisando para saber o que acontece. Tente fazer um teste com o saveOrUpdate SEM o refresh e nos avise. Merge faz um merge do estado do objeto com a base de dados. Talvez por isso você precise desse refresh. Isso pode ser influenciado pela configuração do seu hibernate/mapeamento.

[quote=garcia-jj]Sim, aqui tem um post que citei sobre caches. No link você pode entender sobre eles: http://www.guj.com.br/posts/list/140145.java#754723.

Cache de primeiro nivel é sua propria sessão, ou seja, se você tem um objeto na sua session e por acaso você fizer uma busca ele nem chega a ir no database, e pega a instancia de sua session.

Segundo nivel são usando libs externas (link que te passei) usando diversas libs.

O refresh que você faz apenas torna os objetos “gerenciaveis pela session”. Só analisando para saber o que acontece. Tente fazer um teste com o saveOrUpdate SEM o refresh e nos avise. Merge faz um merge do estado do objeto com a base de dados. Talvez por isso você precise desse refresh. Isso pode ser influenciado pela configuração do seu hibernate/mapeamento.[/quote]

Obrigado pelas informações garcia-jj.

Com relação a realizar o teste com o método saveOrUpdate, acredito que não seja possível pois estou utilizando o EntityManager ao invés da Session do Hibernate. (me corrijam se falei besteira hehe)

[]'s

keyboarder, desculpe meu lapso, você tem razão, hehehehe. EntityManager possui apenas persist e merge.

Enfim, analisei seu código e está tudo correto. Não entendo porque não funciona. Suas entidades estão anotadas corretamente, enfim, aparentemente tudo certo. Você pode mandar o trecho do código onde você busca PessoaRepository.getPessoaFisicaJuridica?

Ahh, só agora lendo bem o post ví uma confusão minha. Sugeri usar saveOrUpdate baseado no merge. Não sei porque foi sugerido você usar merge, já que você não precisa fazer nenhuma persistência por enquanto, apenas leitura.

Abraços

[quote=garcia-jj]keyboarder, desculpe meu lapso, você tem razão, hehehehe. EntityManager possui apenas persist e merge.

Enfim, analisei seu código e está tudo correto. Não entendo porque não funciona. Suas entidades estão anotadas corretamente, enfim, aparentemente tudo certo. Você pode mandar o trecho do código onde você busca PessoaRepository.getPessoaFisicaJuridica?

Ahh, só agora lendo bem o post ví uma confusão minha. Sugeri usar saveOrUpdate baseado no merge. Não sei porque foi sugerido você usar merge, já que você não precisa fazer nenhuma persistência por enquanto, apenas leitura.

Abraços

[/quote]

Como relatei em um post anterior, após chamar o método refresh, consegui recuperar a entidade pessoa já atualizada com o e-mails relacionados a ela.

Pelo que entendi, se eu não chamar o método refresh, ele irá buscar a entidade Pessoa que está em cache e que ainda não teve o relacionamento @OneToMany atualizado, por isso ele não retornava os e-mails associados a pessoa.

O método é este:

[code]public Pessoa getPessoaFisicaJuridica(String busca) {
List pessoaBD = null;
if (!"".equals(busca) && busca != null){
Query q = em.createQuery(“from Pessoa where cnpj = '” + busca.toLowerCase() + “’ OR cpf = '” + busca.toLowerCase() + “’ order by nome”);

		pessoaBD = (List<Pessoa>) q.setMaxResults(10).getResultList();
	
		if (pessoaBD != null && pessoaBD.size() > 0)
			return pessoaBD.get(0);	
	}
	return null;
}[/code]

Obrigado mais uma vez.

[]'s