Erro ao mapear auto relacionamento com JPA

Pessoal, estou tentando mapear esta classe que é um auto relacionamento mais não funciona.

@Entity
@Table(name = “tb_categoria”)
public class Categoria implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;

private String descricao;

@ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JoinColumn(name = "categoria_pai_id",insertable = true, updatable = true, nullable = true)
private Categoria categoriaPai;

apos isso tento persistir, mais ele esta gravando duas vezes, como mostra a imagem abaixo.

sera que os amigos aqui do forum poderiam me ajudar?

obrigado a todos!

Tá salvando 2 vezes quando vc faz o persist dessa classe ou de outra chamando ela?
Tenta tirar o CascadeType.PERSIST para testar.

Oi Freidinger, exatamente ele salva duas vezes quando faço o persiste, quando apenas salvo ela
fiz o teste que vc me aconselhou mais continuo com mesmo problema :pensive:

Me passa o teu código de persistência.

Oi thiago, segue o codigo

public void addCategoria(){

    try {

        categoriaService.addCategoria(this.categoria);
       
    } catch (ServiceException e) {
        Logger.getLogger(CategoriaController.class.getName()).log(Level.SEVERE, null, e);
    } catch (Exception e) {
        Logger.getLogger(CategoriaController.class.getName()).log(Level.SEVERE, null, e);
    }
}

OBS:
como sinalizo a reposta.

ele esta me retornando este erro

aused by: java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.tm.modelo.Categoria.categoriaPai -> com.tm.modelo.Categoria

Me passa o modelo; como você tá alimentando ele e como ta persistindo.

fiz algumas alterções com relação ao de cima, tentando encontrar a solução, mas sem sucesso

segue
@Entity
@Table(name = “tb_categoria”)
public class Categoria implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;

@Column(nullable = false)
private String descricao;

@ManyToOne
@JoinColumn(name = "categoria_pai_id",referencedColumnName = "id")
private Categoria categoriaPai;

@OneToMany(mappedBy = "categoriaPai")
private List<Categoria> subs;

abaixo o persiste

public void addCategoria(){

try {

    categoriaService.addCategoria(this.categoria);
   
} catch (ServiceException e) {
    Logger.getLogger(CategoriaController.class.getName()).log(Level.SEVERE, null, e);
} catch (Exception e) {
    Logger.getLogger(CategoriaController.class.getName()).log(Level.SEVERE, null, e);
}

}

vendo o log ele esta criando dois inserts

@dark_neo, como você está alimentando esse modelo? Cade o código de persistência também? Sem essas duas coisas fica difícil.

eu estou alimentando ele atraves de uma pagina com JSF

segue

<h:panelGroup id=“painelCliente” layout=“block” styleClass=“ui-fluid”>
<p:panelGrid columns=“2” id=“painelCategoria” style="margin-top: 20px"
layout="grid"
columnClasses=“ui-grid-col-2, ui-grid-col-4, ui-grid-col-2, ui-grid-col-4”>

                <p:outputLabel value="Nome:" for="DescriçãoCat"/> 
                <p:inputText id="DescriçãoCat" value="#{categoriaController.categoria.descricao}"/> 
                    
            </p:panelGrid>
            <h5>Categorias</h5>
            <p:panelGrid columns="2" id="painelSubCategoria" style="margin-top: 20px" 
                         layout="grid" 
                         columnClasses="ui-grid-col-2, ui-grid-col-4, ui-grid-col-2, ui-grid-col-4">
                             
                <p:outputLabel value="Categoria pai:" for="pai"/> 
                <p:selectOneMenu id="pai" value="#{categoriaController.categoria.categoriaPai.id}" >
                    <f:selectItem />
                    <f:selectItems var="categoria" itemValue="#{categoria.id}" 
                                   itemLabel="#{categoria.descricao}"
                                   value="#{categoriaController.popularCategoria()}"/>
                </p:selectOneMenu>

abc!!

Você tá usando o persist(object.class, objectId) do entityManager?

so pra vc entender

na tela eu tenho um campo inputText e um SelectItem, o que eu estou fazendo é:

quando eu quero criar uma categoria nova eu apenas digito o nome da categoria e nao seleciono nehum pai na caixinha, quando eu quero associar um subcategoria a um pai eu escrevo este nova sub categoria no compo texto e seleciono o pai no selectItem, informo que na segunda opção funciona perfeitamente o problema ocorre quando nao tem um pai cadastrado na base, ou seja estiver cadastrando ele pela primeira vez.

se nao conseguiu entender me fala que tento explicar melhor

abc!!

não estou usando o merge

public T save(T entity) {
entity = manager.merge(entity);
return entity;
}

Realmente tá difícil de entender o que tu ta querendo fazer. Pq merge e persist são coisas diferentes.

Vou dar um chute.

Você fez um autorelacionamento de uma tabela. Ou seja, você criou uma chave estrangeria que referencia ela mesma.

No primeiro exemplo, o da tabela, que você deu; entendo que você fez um persist com o Id 7, porém como a chave estrangeira tinha o id 6, o hibernate fez esse persist também.

O Hibernate não errou. Isso é algo dele mesmo. Você usou o CascadeType.PERSIST. Quando você salva o filho, ele antes salva o pai. Nesse caso, como você salvou o id 7 e dentro dele tinha o id 6, antes ele então salvou o 6.

Recomendo tirar todos os cascades que existirem.

Antes de mais nada, lembre-se. Para salvar o Categoria com o id 7 referenciando o 6, o 6 precisa estar criado.

Outra solução seria um insert com TypedQuery.

Então thiago imagina o seguinte cenário:

Cenário 1

na tela que te informei tenho dois campos um inputText e um SelectItem certo
o usuario entra nesta tela e quer cadastrar uma nova categoria;
usuario digita no campo o nome dessa categoria, como é a primeira vez esta categoria sera o topo da hierarquia ou seja o id_pai será nulo.

neste cenario ele persiste duas vezes, como o mapeamento que fiz no inicio deste post

Cenário 2

o usuario que cadastrar uma nova categoria e para o Pai criado no cenário 1, ex:

digamos que ele criou a categoria Refrigerante, ela esta no topo da hierarquia,usuário informa nova subcategoria com o nome de Coca-cola e e seleciona o pai refrigerante e clica no botão salvar.

neste cenário o save é feito corretamente.

resumindo o que eu nao estou conseguindo entender é pq ele nao consegue salvar uma categoria quando ela esta no topo da hierarquia?

No seu cenário 1, como eu disse, você está usando: @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})

Remova o CascadeType.Persiste o CascadeType.Mergee tenta repetir ele.

Não esqueça de colocar como NULL o categoriaPai.

@dark_neo , conseguiu?

fiz da forma que vc falou mais continua dando o mesmo erro.

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = “id”, nullable = false)
private Integer id;

@Column(nullable = false)
private String descricao;

@ManyToOne
@JoinColumn(name = "categoria_pai_id", nullable = true)
private Categoria categoriaPai;

Caused by: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing: com.tm.modelo.Categoria.categoriaPai -> com.tm.modelo.Categoria

Te mandei meu skype. Me adiciona que eu te ajudo por lá. Por aqui tá difícil de entender o que você tá fazendo e como tá fazendo.

Qual foi a solução para o problema? também estou com a mesma dificuldade.