"Detached Entity Passed to Persist Error" with JPA/Ejb Code

detached entity passed to persist error with JPA/EJB code

ERD

Let's say you have two entities Album and Photo. Album contains many photos, so it's a one to many relationship.

Album class

@Entity
public class Album {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Integer albumId;

String albumName;

@OneToMany(targetEntity=Photo.class,mappedBy="album",cascade={CascadeType.ALL},orphanRemoval=true)
Set<Photo> photos = new HashSet<Photo>();
}

Photo class

@Entity
public class Photo{
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Integer photo_id;

String photoName;

@ManyToOne(targetEntity=Album.class)
@JoinColumn(name="album_id")
Album album;

}

What you have to do before persist or merge is to set the Album reference in each photos.

        Album myAlbum = new Album();
Photo photo1 = new Photo();
Photo photo2 = new Photo();

photo1.setAlbum(myAlbum);
photo2.setAlbum(myAlbum);

That is how to attach the related entity before you persist or merge.

PersistentObjectException: detached entity passed to persist thrown by JPA and Hibernate

This is a typical bidirectional consistency problem. It is well discussed in this link as well as this link.

As per the articles in the previous 2 links you need to fix your setters in both sides of the bidirectional relationship. An example setter for the One side is in this link.

An example setter for the Many side is in this link.

After you correct your setters you want to declare the Entity access type to be "Property". Best practice to declare "Property" access type is to move ALL the annotations from the member properties to the corresponding getters. A big word of caution is not to mix "Field" and "Property" access types within the entity class otherwise the behavior is undefined by the JSR-317 specifications.

detached entity passed to persist Kotlin

You've defined the id field as auto generated but assigned it a default value. When hibernate tries to persist it, it thinks that the object represents an underlying row in database because it already has an id. So, it considers it a detached object.
The persist() method doesn't work on a detached object.
You can set the default value of id to null to resolve this issue.

detached entity passed to persist - on findByMethod

This behavior depends on FlushMode of EntityManager.

The FlushMode defines when new entities and your changes on existing ones get written to the database, in other words, defines when flush() operation is performing.

The flush process synchronizes database state with session state by
detecting state changes and executing SQL statements.

The JPA specification defines the FlushModeType.AUTO as the default flush mode. It flushes the persistence context in next situations:

  • before the transaction gets committed
  • before executing a query that uses any database table for which your persistence context contains any pending changes.

According to documentation:

AUTO

The Session is sometimes flushed before query execution in order to
ensure that queries never return stale state. This is the default
flush mode.

Summary:
So in your example, we have default FlushMode.AUTO, during the query execution framework performs flush() operation, this is the reason.


FlushMode.AUTO is fixing consistency issues on a query basis, but on other hand can lead to unpredictable flush operations and as result performance problems in the case of huge services. I recommend to change FlushMode to COMMIT, when you do not need to resolve consistency issues. The FlushModeType.COMMIT requires a flush before committing the transaction but doesn’t define what needs to happen before executing a query. Executing any query doesn’t flush any pending changes.

Changing FlushMode:
1. Globally for server add to properties

spring.jpa.properties.org.hibernate.flushMode=COMMIT

2. Set for specific query

Query quey = entityManager.createQuery("SELECT * from Foo");
quey.setFlushMode(FlushModeType.COMMIT);

3. Set for Session
Inject EntityManager into your service.

   @Transactional
fun save(name:String, city: String){
entityManager.setFlushMode(FlushModeType.COMMIT)
...
}

Detailed described at How does AUTO flush strategy work in JPA and Hibernate



Related Topics



Leave a reply



Submit