"detached entity passed to persist" when setting id explicitly and usage of @GeneratedValue on column

Viewed 32

I have an entity with auto-generated values :

@Entity
public class MyEntity {

    @Id
    @GeneratedValue(generator = "myuuid-generator")
    @GenericGenerator(name = "myuuid-generator", strategy = "com.foo.hibernate.MyUUIDGenerator")
    @Column
    @AccessType(value = "property")
    private String id;

    // ...
}

I some cases I want to set the ID explicitly, so I have overriden the identifier generator :

public class MyUUIDGenerator extends UUIDHexGenerator {

    @Override
    public Serializable generate(SessionImplementor session, Object object) {
        MyEntity entity = (MyEntity) object;
        String explicitId = entity.getId();
        if (explicitId != null) {
            return explicitId;
        }
        return super.generate(session, object);
    }

}

Unfortunatly that does not work :

MyEntity entity = new MyEntity();
entity.setId("bar");
session.persist(entity);
session.flush();

Error :

org.hibernate.PersistentObjectException: detached entity passed to persist: com.foo.MyEntity at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:124) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:58) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:775) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:748) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:753)

I use Hibernate 5.0.10.Final.

What's wrong with my code ?

Thanks for your help.

1 Answers

Reason:
You pass an entity with a filled Id the Hibernate considers that this entity already exists and determines the entity as detached.

Solution 1: Hibernate
In the case of Hibernate we can use Session.save instead of Session.persist. Save method has the ability to handle manually defined Id and still determine an entity as a transient.
According to documentation:

Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.) This operation cascades to associated instances if the association is mapped with cascade="save-update"

        MyEntity entity = new MyEntity();
        entity.setId("bar");
        session.save(entity);
        session.flush();

Solution 2: JPA
Hibernate Save method doesn't conform to the JPA specification.
In the case of JPA we can use EntityManager.merge instead of EntityManager.persist.
Merge method copies the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance.
So merging is the process of taking an incoming entity instance that is in the detached state and copying its data over onto a new managed instance.

        MyEntity entity = new MyEntity();
        entity.setId("bar");
        entityManager.merge(entity);
        entityManager.flush();
Related