Erro ao mapear auto relacionamento com JPA

19 respostas
D

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!

19 Respostas

F

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

D

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:

T

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

D

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.

D

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

T

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

D

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

T

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

D

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!!

T

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

D

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!!

D

não estou usando o merge

public T save(T entity) {

entity = manager.merge(entity);

return entity;

}
T

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.

D

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?

T

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.

T

@dark_neo , conseguiu?

D

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

T

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.

K

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

Criado 11 de agosto de 2016
Ultima resposta 18 de abr. de 2017
Respostas 19
Participantes 4