1
resposta

[Dúvida] Erro: (.TransientObjectException: persistent instance references an unsaved transient instance)

Seguindo os exercícios do projeto de "gerenciar-pedidos", tentei fazer algo um pouco diferente, ao fazer um menu assim como no "screen-match". Optei por não fazer a classe de Pedidos para evitar mais complicações.

Classe Main (tirei todos os imports e os metodos de cadastrar categoria e fornecedor)

package com.example.gerenciar_pedidos;

@SpringBootApplication
public class GerenciarPedidosApplication implements CommandLineRunner {

    @Autowired
    private ProdutoRepository produtoRepository;
    @Autowired
    private CategoriaRepository categoriaRepository;
    @Autowired
    private FornecedorRepository fornecedorRepository;
    Scanner scanner = new Scanner(System.in);


    public static void main(String[] args) {
        SpringApplication.run(GerenciarPedidosApplication.class, args);
    }

    @Override
    public void run(String... args) {

        var opcao = -1;
        while (opcao != 0) {
            var menu = """
                    1 - Cadastrar categoria
                    2 - Cadastrar fornecedor
                    3 - Cadastrar produto
                    
                    0 - Sair
                    """;

            System.out.print(menu);
            opcao = scanner.nextInt();
            scanner.nextLine();

            switch (opcao) {
                case 1:
                    cadastrarCategoria();
                    break;
                case 2:
                    cadastrarFornecedor();
                    break;
                case 3:
                    cadastrarProduto();
                    break;
                case 0:
                    System.out.println("Saindo...");
                    break;
                default:
                    System.out.println("Opção inválida");
            }
        }


    }

    private void cadastrarProduto() {
        System.out.println("\nProdutos já cadastrados: ");
        List<Produto> produtoCadastrados = produtoRepository.findAll();
        produtoCadastrados.forEach(System.out::println);

        System.out.print("Digite o nome do produto: ");
        String produtoUser = scanner.next();

        System.out.print("Digite o preço: ");
        var precoUser = scanner.nextDouble();

        System.out.print("Digite a categoria associada: ");
        var categoriaProduto = scanner.next();
        Categoria categoriaAssociada = new Categoria(categoriaProduto);


        Produto produto = new Produto(produtoUser, precoUser, categoriaAssociada);
        produtoRepository.save(produto);
    }


}

Classe Produto

package com.example.gerenciar_pedidos.model;

import jakarta.persistence.*;

@Entity
public class Produto {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(nullable = false, unique = true)
    private String nome;
    @Column(name = "valor")
    private Double preco;
    @ManyToOne
    @JoinColumn(name = "categoria_id")
    private Categoria categoria;
    @Transient
    @ManyToOne
    @JoinColumn(name = "fornecedor_id")
    private Fornecedor fornecedor;

    public Produto(String nome, Double preco, Categoria categoria) {
        this.nome = nome;
        this.preco = preco;
        this.categoria = categoria;
    }

Erro

org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientObjectException: persistent instance references an unsaved transient instance of 'com.example.gerenciar_pedidos.model.Categoria' (save the transient instance before flushing)

at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:368) ~[spring-orm-6.2.1.jar:6.2.1]
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:246) ~[spring-orm-6.2.1.jar:6.2.1]
    at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:566) ~[spring-orm-6.2.1.jar:6.2.1]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:795) ~[spring-tx-6.2.1.jar:6.2.1]
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:758) ~[spring-tx-6.2.1.jar:6.2.1]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:698) ~[spring-tx-6.2.1.jar:6.2.1]
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:416) ~[spring-tx-6.2.1.jar:6.2.1]
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-6.2.1.jar:6.2.1]

IMPORTANTE: A categoria cadastrada existe no banco de dados

O comportamento desejado é que os IDs fossem relacionados.

1 resposta

Olá Gustavo! Tudo bem?

O erro que você está enfrentando, TransientObjectException, geralmente ocorre quando você tenta salvar uma entidade que possui uma referência a outra entidade que ainda não foi salva no banco de dados. No seu caso, parece que isso está acontecendo com a Categoria associada ao Produto.

Mesmo que a categoria já exista no banco de dados, o problema pode estar na forma como você está criando a instância da Categoria na memória. Quando você faz new Categoria(categoriaProduto), está criando uma nova instância que o JPA não reconhece como uma entidade gerenciada.

Para resolver isso, você deve buscar a Categoria existente no banco de dados antes de associá-la ao Produto. Você pode fazer isso utilizando o CategoriaRepository para buscar a categoria pelo nome ou pelo ID. Aqui está um exemplo de como você pode modificar o método cadastrarProduto:

private void cadastrarProduto() {
    System.out.println("\nProdutos já cadastrados: ");
    List<Produto> produtoCadastrados = produtoRepository.findAll();
    produtoCadastrados.forEach(System.out::println);

    System.out.print("Digite o nome do produto: ");
    String produtoUser = scanner.next();

    System.out.print("Digite o preço: ");
    var precoUser = scanner.nextDouble();

    System.out.print("Digite a categoria associada: ");
    var categoriaProduto = scanner.next();

    // Buscar a categoria pelo nome ou ID no banco de dados
    Categoria categoriaAssociada = categoriaRepository.findByNome(categoriaProduto);
    if (categoriaAssociada == null) {
        System.out.println("Categoria não encontrada!");
        return;
    }

    Produto produto = new Produto(produtoUser, precoUser, categoriaAssociada);
    produtoRepository.save(produto);
}

Certifique-se de que o CategoriaRepository tem um método para buscar a categoria pelo nome, algo como findByNome(String nome). Se não tiver, você pode adicionar um método de consulta personalizado no repositório.

Espero ter ajudado e bons estudos!

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓.