NonUniqueObjectException - hibernate

Bom dia Pessoal.

Tenho uma tela de pedidos em jsf, onde se o valor do pedido for 0 (zero) eu preciso excluir o pedido.
Utilizo hibernate com os pojos, mapeamentos e implementações estão em um projeto separado do projeto web.
o pedido é incluido e gravado normalmente, mas quando tento excluir o pedido, recebo a exception abaixo:

WARNING: #{pedidosBean.excluiPedido}: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [br.com.jvmsoftware.deliveryjava.mapeamento.DlvPedidos#85]
javax.faces.FacesException: #{pedidosBean.excluiPedido}: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [br.com.jvmsoftware.deliveryjava.mapeamento.DlvPedidos#85]
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118)
	at javax.faces.component.UICommand.broadcast(UICommand.java:315)
	at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794)
	at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259)
	at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81)
	at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
	at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
	at javax.faces.webapp.FacesServlet.service(FacesServlet.java:409)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
	at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:861)
	at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:606)
	at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
	at java.lang.Thread.run(Thread.java:724)
Caused by: javax.faces.el.EvaluationException: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [br.com.jvmsoftware.deliveryjava.mapeamento.DlvPedidos#85]
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102)
	at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102)
	... 19 more
Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [br.com.jvmsoftware.deliveryjava.mapeamento.DlvPedidos#85]
	at org.hibernate.engine.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:590)
	at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:99)
	at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:52)
	at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:766)
	at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:744)
	at br.com.jvmsoftware.deliveryjava.implement.DlvPedidosImp.excluiPedido(DlvPedidosImp.java:53)
	at br.com.jvmsoftware.delivery.bean.PedidosBean.excluiPedido(PedidosBean.java:199)
	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:606)
	at org.apache.el.parser.AstValue.invoke(AstValue.java:191)
	at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
	at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105)
	at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88)
	... 20 more

Meu objeto pedido é único na bean, por isso, não estou entendendo o porque do erro (metodo excluiPedido)

meu bean:

@ManagedBean
@SessionScoped
public class PedidosBean implements Serializable {

    private AcsUsuariosImp usuImp = new AcsUsuariosImp();
    private DlvCardapiosImp cardImp = new DlvCardapiosImp();
    private DlvPedidosImp pedImp = new DlvPedidosImp();
    private DlvConfigImp confImp = new DlvConfigImp();
    private DlvSituacaoPedidoImp sitPedImp = new DlvSituacaoPedidoImp();
    private DlvTipoPedidoImp tipoPedImp = new DlvTipoPedidoImp();
    private DlvFormaPagamentosImp forPagImp = new DlvFormaPagamentosImp();
    private DlvTipoPagamentosImp tipoPagImp = new DlvTipoPagamentosImp();
    private DlvEntregadoresImp entImp = new DlvEntregadoresImp();
    private AcsClientesImp cliImp = new AcsClientesImp();
    public AcsClientes cliente = new AcsClientes();
    public DlvPedidos pedido = new DlvPedidos();
    public AcsUsuarios usuario = new AcsUsuarios();
    public DlvPedidoItens itemPedido = new DlvPedidoItens();
    public DlvConfig config = new DlvConfig();
    public Date diaPedido = new java.util.Date();
    public Boolean exibeTabItens = false;
    public Boolean incluiPedido = true;
    public Boolean alteraPedido = false;
    public Boolean imprimePedido = false;
    public List<DlvCardapios> listCardapios;
    public List<DlvPedidos> listPedidos;
    public List<AcsClientes> listClientes;
    public List<DlvEntregadores> listEntregadores;
    public List<DlvSituacaoPedido> listSituacaoPedidos;
    public List<DlvTipoPedido> listTipoPedidos;
    public List<DlvFormaPagamento> listFormaPagamentos;
    public List<DlvTipoPagamento> listTipoPagamentos;
    public List<DlvPedidoItens> listItensPedido;
    public String psw = null;
    FacesMessage msg = null;
    
    
    public PedidosBean() {
        // nova instancia de ClientesBean
        diaPedido = new java.util.Date();
        listSituacaoPedidos = sitPedImp.listSituacaoPedido();
        listTipoPedidos = tipoPedImp.listTipoPedidoNotDif();
        listFormaPagamentos = forPagImp.listFormaPagamento();
        listTipoPagamentos = tipoPagImp.listTipoPagamento();
    }

    public void startIncluiPedido() {
        System.out.println("PedidosBean.startIncluiPedido: ");
        pedido = new DlvPedidos();
        DlvSituacaoPedido sit = sitPedImp.getById(1);
        DlvTipoPedido tipoPedido = tipoPedImp.getById(1);
        exibeTabItens = false;
        alteraPedido = false;
        incluiPedido = true;
        listItensPedido = new ArrayList<DlvPedidoItens>();
        config = confImp.getConfig(usuario.getAcsEmpresa());
        listEntregadores = entImp.listEntregadores(usuario.getAcsEmpresa(), new Long(0), "", new Long(0));
        pedido.setAcsEmpresa(usuario.getAcsEmpresa());
        pedido.setDlvSituacaoPedido(sit);
        pedido.setDlvTipoPedido(tipoPedido);
        pedido.setAcsClientes(cliente);
    }

    public void incluiPedido() {
        System.out.println("PedidosBean.incluiPedido: ");
        pedido.setTotalPedido(BigDecimal.ZERO);
        pedido.setValorPago(BigDecimal.ZERO);
        pedido.setTroco(BigDecimal.ZERO);
        pedido.setDtHoraPedido(new java.util.Date());
        pedido.setAcsUsuariosByUsuarioPedido(usuario);
        pedImp.incluiPedidos(pedido);
        //System.out.println("PedidosBean.incluiPedido: pedido: " + pedido.getIdPedido());
        // exibe tabela de itens
        exibeTabItens = true;
        // novo item pedido
        itemPedido = new DlvPedidoItens();
        alteraPedido = true;
        incluiPedido = false;
        listCardapios();
        listPedidos();
    }
  
    public void alteraPedido() {
        System.out.println("PedidosBean.incluiPedido: ");
        // exibe tabela de itens
        exibeTabItens = true;
        imprimePedido = true;
        // novo item pedido
        itemPedido = new DlvPedidoItens();
        calculaValorPedido();
        System.out.println("PedidosBean.alteraPedido");
        if(pedido.getValorPago().compareTo(BigDecimal.ZERO) == 0 || pedido.getValorPago().compareTo(pedido.getTotalPedido()) < 0) {
            System.out.println("PedidosBean.alteraPedido - valor pago = 0");
            pedido.setValorPago(pedido.getTotalPedido());
        }
        calculaTroco();
        listCardapios();
        listItensPedido();
        // atualiza lista de pedidos
        listPedidos();
        pedImp.salvaPedidos(pedido);
    }

    public void excluiPedido() {
        System.out.println("PedidosBean.excluiPedido: idPedido: " + pedido.getIdPedido());
        System.out.println("PedidosBean.excluiPedido: totalPedido: " + pedido.getTotalPedido());
        exibeTabItens = false;
        if (pedido.getIdPedido() != null ) {
            if (pedido.getTotalPedido() == null || pedido.getTotalPedido().compareTo(BigDecimal.ZERO) == 0) {
                System.out.println("if (pedido.getIdPedido() == null) - idPedido: " + pedido.getIdPedido());
                listItensPedido();
                if (!listItensPedido.isEmpty()) {
                    for (int i = 0; i < listItensPedido.size(); i++) {
                        itemPedido = listItensPedido.get(i);
                        excluiItenPedido();
                    }
                }
                pedImp.salvaPedidos(pedido);
                pedImp.excluiPedido(pedido);                                                   // o problema é nesta chamada
            } else {
                if (!"CONSUMIDOR".equals(pedido.getAcsClientes().getNome())) {
                    // verifica itens de pedido
                    listItensPedido();
                    calculaValorPedido();
                }
            pedImp.salvaPedidos(pedido);
            }
        alteraPedido = false;
        incluiPedido = true;
        pedido = new DlvPedidos();
        cliente = new AcsClientes();
        listPedidos();
        }
    }

    public void cancelaPedido() {
        ...
    }
    
    public void incluiItenPedido() {
        ...
    }
    
    public void excluiItenPedido() {
        ...
    }
    
    public void listPedidos(){
        ...
    }
    
    public void listItensPedido(){
        ...
    } 
        
    public void listCardapios(){
        ...
    }
    
    public void calculaValorPedido() {
        ...
    }

    
    public void calculaTroco() {
        ...
    }
// getters e setters
.....
}

minha classe de implementação do hibernate:

public class DlvPedidosImp implements Serializable {
    
    SessionFactory sf = getSessionFactory();
    Session session = sf.openSession();
    
    public DlvPedidosImp() {
    }
    
    public void salvaItemPedido(DlvPedidoItens itemPedido) {
        session.beginTransaction();
        session.flush();
        session.saveOrUpdate(itemPedido);
        session.beginTransaction().commit();
        session.flush();
    }
        
    public void incluiPedidos(DlvPedidos pedido) {
        session.beginTransaction();
        session.saveOrUpdate(pedido);
        session.beginTransaction().commit();
        session.flush();
    }
    
    public void salvaPedidos(DlvPedidos pedido) {
        session.beginTransaction();
        session.merge(pedido);
        session.beginTransaction().commit();
        session.flush();
    }
        
    public void excluiPedido(DlvPedidos pedido) {
        session.beginTransaction();
        session.delete(pedido);                                  // O ERRO É GERADO NESTE PONTO
        session.beginTransaction().commit();
        session.flush();
    }
}

Já fiz a chamada por um arquivo de testes, e funciona:

    public void pedido() {
        AcsUsuariosImp usuImp = new AcsUsuariosImp();
        DlvSituacaoPedidoImp sitImp = new DlvSituacaoPedidoImp();
        DlvFormaPagamentosImp forImp = new DlvFormaPagamentosImp();
        DlvTipoPagamentosImp tPagImp = new DlvTipoPagamentosImp();
        DlvTipoPedidoImp tPedImp = new DlvTipoPedidoImp();
        AcsUsuarios usuario = usuImp.getUsuariosById(1);
        DlvSituacaoPedido sit = sitImp.getById(1);
        DlvFormaPagamento form = forImp.getById(1);
        DlvTipoPagamento tPag = tPagImp.getById(1);
        DlvTipoPedido tPed = tPedImp.getById(1);
        cliente = cliImp.getById(10);
        empresa = empImp.getEmpresaById(1);
        pedido.setAcsClientes(cliente);
        pedido.setAcsEmpresa(empresa);
        pedido.setAcsUsuariosByUsuarioPedido(usuario);
        pedido.setDtHoraPedido(new java.util.Date());
        pedido.setDlvSituacaoPedido(sit);
        pedido.setDlvFormaPagamento(form);
        pedido.setDlvTipoPagamento(tPag);
        pedido.setDlvTipoPedido(tPed);
        pedImp.incluiPedidos(pedido);
        System.out.println("pedido incluido com sucesso!");
        pedImp.excluiPedido(pedido);
        System.out.println("pedido excluido com sucesso!");
    }

O problema só acontece quando estou utilizando a tela de pedidos (.xhtml)

Como contornar este problema??

Você está tendo este problema por que está alterando algum elemento que foi previamente carregado e associado a sessão do hibernate.
Provavelmente criou uma referência (um novo objeto) a partir do que obteve na consulta e o alterou.

O Objeto é criado na instanciação do bean e depois eu seto alguma propriedades do objeto, de acordo com o que foi preenchido na página.

Quando salvo o objeto (utilizando o metodo da classe de implementação), tenho sucesso. O problema é somente quando tento excluir o objeto do banco (o objeto que acabou de ser salvo).

Isso funcionava na estrutura do projeto que eu tinha anteriormente (projeto único com hibernate e web no mesmo projeto).

A estrutura do meu projeto foi alterada, deixei um projeto somente com hibernate e um projeto somente com web, importando o projeto hibernate nas bibliotecas do projeto web.

Alguém consegue ajudar?

Entender o seu código assim para encontrar exatamente onde está o problema vai ser difícil. O que vou tentar fazer aqui é te explicar o que esse erro significa, e assim quem sabe você consiga encontra-lo.

Em Hibernate/JPA existe o que é chamado de “persistence context”, por muitos também chamado de “cache de primeiro nivel”. Quando você faz uma pesquisa simples, usando um “session.get()” ou “entityManager.find()”, o objeto que é retornado fica em um cache. Então enquanto durar aquele persistence context, todas as tentativas de buscar o mesmo objeto com chamadas parecidas, não vão nem gerar uma chamada ao banco. O hibernate vai simplesmente devolver o mesmo objeto que já está no cache!

//somente na primeira chamada um SQL é feito Cliente cliente1 = session.get(Cliente.class, new Integer(66)); Cliente cliente2 = session.get(Cliente.class, new Integer(66)); //aqui nao acontece nenhum SQL no banco

A segunda chamada acima vai apontar a referencia ‘cliente2’ para o mesmo objeto Cliente que foi retornado na primeira chamada. No persistence context só existe um único objeto! Entretanto vão existir casos, tipo o seu, onde por alguma lógica maluca você conseguiu colocar dois objetos diferentes, onde ambos representam o mesmo registro de banco, no persistence context. E nessa hora o hibernate se confunde pois ele não vai saber qual dos dois objetos sincronizar com o banco de dados quando o flush ocorrer.

Um exemplo mais claro:

[code]Cliente clienteDoBanco = (Categoria) session.get(Cliente.class, new Integer(66));
clienteDoBanco.setNome(“Nome Antigo”);

Cliente clienteNaMao = new Cliente();
clienteNaMao.setId(66);
clienteNaMao.setNome(“Novo Nome”);

session.update(clienteNaMao); //erro[/code]

Observe que no código acima, vão existir DOIS OBJETOS que representam o mesmo cliente de código 66 na “memória” do hibernate. Quando isso ocorre, ele não tem como saber qual dos dois deve ter seu estado sincronizado com o banco quando a transação atual for “flusheada”.

Deve ser esse o seu problema. Em algum lugar na sua lógica você “clonou” os objetos que ta tentando deletar. Quando chega a hora do flush, o hibernate não sabe o que fazer pois existem dois e ele não pode escolher aleatoriamente. A dúvida que muita gente tem é porque isso ocorre já que você só chamou explicitamente o update para um dos objetos, e não para os dois. Mas lembre-se que o hibernate trabalha com um conceito chamado “dirty checking”, onde qualquer objeto gerenciado por ele, mesmo que não exista uma chamada explícita ao update(), será alterado no banco caso seu estado tenha sido modificado.

É isso. Espero ter ajudado.