Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

[Dúvida] org.hibernate.PersistentObjectException: detached entity passed to persist

Eu possuo o DTO e uma entidade, onde eu consigo alterar e remover itens normalmente. Acontece que quando eu tento adicionar novos itens a uma lista que já esta salva no banco de dados eu tenho recebido a seguinte exceção:

org.hibernate.PersistentObjectException: detached entity passed to persist: br.com.projetonotafiscal.notafiscal.Entity.Itens

Minha classe onde persisto os itens é a classe NOTA que esta da seguinte forma:

@Entity(name = "Nota")
@Table(name = "nota")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Nota {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id_cliente")
    private Cliente cliente;

    @JsonManagedReference
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "nota", cascade = CascadeType.ALL)
    private List<Itens> itens;

    private Integer numero;
    private Date data;
    private BigDecimal valor_total;

Minha classe Itens:

@Entity(name = "Itens")
@Table(name = "itens")
public class Itens {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "id_produto")
    private Produto produto;

    @JsonBackReference
    @ManyToOne(fetch = FetchType.LAZY)
    private Nota nota;
    private Integer ordenacao;
    private BigDecimal quantidade;
    private BigDecimal valor_total;

Na minha service, o método de atualizar esta assim:

public void atualizar(NotaDTO dto) {
        Nota nota = repository.getReferenceById(dto.getId());
        List<Itens> list = itensRepository.findAllByIdItens(nota);
        BigDecimal somaTotal = BigDecimal.ZERO;

        for (Itens item : list) { //Percorrendo a lista de itens que já esta salva no banco de dados
            if (!dto.getItens().contains(itensRepository.getReferenceById(item.getId()))) {
                itensRepository.deleteById(item.getId());
            }
            for (Itens itemDTO : dto.getItens()) { //Percorrendo a lista do dto que estou recebendo
                if (itemDTO.getId() == null) {
                    int ordenacao = itensRepository.findByUltimaOrdencao(nota);
                    item.setOrdenacao(ordenacao++);

                    Long valorUnitario = produtoRepository.findByValorUnitarioProduto(item.getProduto().getId());

                    item.setValor_total(item.getQuantidade().multiply(BigDecimal.valueOf(valorUnitario)));
                    somaTotal = somaTotal.add(item.getValor_total());

                    item.setNota(nota);

                    break;
                }
                if (item.getId() == itemDTO.getId()) {
                    item.setQuantidade(itemDTO.getQuantidade());

                    break;
                }
            }
            Long valorUnitario = produtoRepository.findByValorUnitarioProduto(item.getProduto().getId());

            item.setValor_total(item.getQuantidade().multiply(BigDecimal.valueOf(valorUnitario)));
            somaTotal = somaTotal.add(item.getValor_total());
        }

        nota.setValor_total(somaTotal);
        nota.atualizar(dto);
    }

Pelo que andei estudando o erro acontece com o cascade, porém tentei de todas as formas e o erro permanece. Quando uso o cascade = CascadeType.DETACH não acontece o insert e muito menos o update nas duas tabelas. Como resolver esta situação?

4 respostas

Olá Natali, tudo bem?

O erro que você está enfrentando, "org.hibernate.PersistentObjectException: detached entity passed to persist", ocorre quando você está tentando persistir uma entidade desanexada (detached entity). Isso significa que a entidade já existe no banco de dados e foi carregada em uma sessão do Hibernate, mas você está tentando persisti-la novamente.

No seu código, você está percorrendo a lista de itens existentes no banco de dados e comparando com os itens do DTO que você recebeu. Se um item existente não estiver presente no DTO, você está excluindo-o usando itensRepository.deleteById(item.getId()). No entanto, você também precisa remover esse item da lista itens na entidade Nota antes de persisti-la novamente.

Antes de remover o item existente com itensRepository.deleteById(item.getId()), adicione nota.getItens().remove(item) para remover o item da lista na entidade Nota. Dessa forma, o Hibernate saberá que o item foi removido e não tentará persisti-lo novamente.

Aqui está o trecho atualizado do seu código:

for (Itens item : list) {
    if (!dto.getItens().contains(itensRepository.getReferenceById(item.getId()))) {
        nota.getItens().remove(item); // Remover o item da lista na entidade Nota
        itensRepository.deleteById(item.getId());
    }
    // Restante do código...
}

Certifique-se de fazer essa modificação sempre que remover um item existente da lista itens na entidade Nota. Isso deve resolver o problema de "detached entity passed to persist" que você está enfrentando.

Espero ter ajudado e bons estudos!

Oi Otávio!

Fiz as modificações e mesmo assim o problema continua.... notei que acontece quando na classe nota, no meu método de atualizar:

 public void atualizar(NotaDTO dto) {
        if (dto.getCliente() != null) {
            this.cliente = new Cliente(new ClienteDTO());
        }
        if (dto.getItens() != null) {  //Se eu removo essa linha da o update mas não salva o item novo.
            this.itens = dto.getItens();
        }
        if (dto.getData() != null) {
            this.data = dto.getData();
        }
        if (dto.getValor_total() != null) {
            this.valor_total = dto.getValor_total();
        }
    }

Removi a linha de excluir e tudo, mas mesmo assim se mantenho essa parte o erro continua.

solução!

No seu método atualizar() da classe Nota, você está substituindo a lista de itens existente (this.itens) pela lista de itens do DTO (dto.getItens()) diretamente. Isso pode causar problemas de persistência no Hibernate, pois os itens do DTO são entidades desanexadas.

Uma abordagem mais segura seria percorrer a lista de itens do DTO e adicionar cada item individualmente à lista de itens existente, ao invés de substituí-la completamente. Dessa forma, o Hibernate entenderá que os itens já existentes estão sendo atualizados e os novos itens serão persistidos corretamente.

Aqui está uma sugestão de modificação para o seu método atualizar():

public void atualizar(NotaDTO dto) {
    if (dto.getCliente() != null) {
        this.cliente = new Cliente(new ClienteDTO());
    }
    if (dto.getItens() != null) {
        for (Itens itemDTO : dto.getItens()) {
            if (itemDTO.getId() == null) {
                // Criar e adicionar novo item à lista
                Itens novoItem = new Itens();
                novoItem.setProduto(itemDTO.getProduto());
                novoItem.setQuantidade(itemDTO.getQuantidade());
                novoItem.setValor_total(itemDTO.getValor_total());
                novoItem.setNota(this);
                this.itens.add(novoItem);
            } else {
                // Atualizar item existente
                for (Itens item : this.itens) {
                    if (item.getId().equals(itemDTO.getId())) {
                        item.setQuantidade(itemDTO.getQuantidade());
                        // Outras atualizações de propriedades se necessário
                        break;
                    }
                }
            }
        }
    }
    if (dto.getData() != null) {
        this.data = dto.getData();
    }
    if (dto.getValor_total() != null) {
        this.valor_total = dto.getValor_total();
    }
}

Nessa modificação, percorremos a lista de itens do DTO e, para cada item, verificamos se ele já existe (com base no ID). Se o item não tiver um ID definido, significa que é um novo item e, portanto, criamos uma nova instância de Itens e a adicionamos à lista this.itens. Se o item tiver um ID definido, procuramos o item correspondente na lista this.itens e atualizamos suas propriedades.

Dessa forma, você mantém os itens existentes no banco de dados corretamente associados à entidade Nota e adiciona novos itens de forma adequada.

Huum entendi!!!! Muito obrigada pela ajuda.