Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hibernate - how to save parent with detached child

Tags:

orm

hibernate

jpa

I am sending an object from UI. This object is going to be created with a reference to an existing child.

This is simple illustration for this relation.

class ParentEntity {
    @Id
    Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private ChildEntity child;
}

class ChildEntity {
    @Id
    Long id;
}



ChildEntity child = new ChildEntity();
child.setId(1);
//parentEntity is created based on data sent from UI
parentEntity.setChild(child);

When I save this object, Hibernate gives me " org.hibernate.TransientPropertyValueException: object references an unsaved transient instance ".

I don't have to save a child as I do not change child at all. Just need to save child's id in parent's table.

I've tried to use few CascadeType, but none of them worked.

like image 346
Chris Avatar asked Aug 21 '15 00:08

Chris


2 Answers

Just use the proxy for the child:

parentEntity.setChild(entityManager.getReference(ChildEntity.class, childId));

The point here is to use EntityManager.getReference:

Get an instance, whose state may be lazily fetched.

Hibernate will create the proxy containing only the id without going to the database.

like image 70
Dragan Bozanovic Avatar answered Nov 03 '22 19:11

Dragan Bozanovic


You have to decide how do you want to persist ChildEntity with ParentEntity.

  1. If you always want to persist child only by id then you can do like this and FK will block you if its invalid child.

    class ParentEntity {
        @Id
        Long id;
    
        //To directly load/insert/update childId only
        @Column("child_id")
        private Long childId;
    
       // To load the child entity with parent, Note that insertable/updatable MUST be false
       // as we are inserting/updating = true by default on childId column mapping
       @ManyToOne(fetch = FetchType.LAZY)
       @JoinColumn(name = "child_id", insertable = false, updatable = false)
       private ChildEntity child;
    }
    parentEntity.setChildId(childId);
    
  2. But if you want to persist child using your existing model where child is mapped as an object, then you have to fetch the child and set it. parentEntity.setChild(childRepository.findOne(childId)); or you can also write a custom DAO method for getOrCreateChild which can create a new child or find existing one and return it so that you can persist child first.

In your case when you use new keyword for creating childEntity, hibernate will always consider it as detached and tries to persist.

like image 26
Anudeep Gade Avatar answered Nov 03 '22 19:11

Anudeep Gade