siamii
siamii

Reputation: 24114

PersistentObjectException: detached entity passed to persist

In my app I'm connected to a websocket. The socket is sending JSON data. As it is becoming available I process it in a different Job. In each job, I convert the JSON to a model object with GSON. There are 3 types of models, all independent, and with one common timestamp field in a MappedSuperclass. They don't have an id explicit field. I call save on the model and in 1 out of 1000 times a "PersistentObjectException: detached entity passed to persist" is thrown.

The multiple jobs are called from a start up Job that connects to the websocket. The library I'm using creates a different thread for each incoming message. I then convert the thread into a job before saving to the database. I do this, because otherwise another PersistentObjectException is thrown about changing the id from 1 to 2 or something similar, if I allow the original thread to call save.

I also have another job running that accesses the database at the same time. What could be wrong?

@Override
public void onMessage(final WebSocketMessage message){
        new Job() {
            @Override
            public void doJob() {
                processMessage(message.getText());
            }
        }.now();

}

 public void processMessage(String message) {
        Appointment appointment = new Gson().fromJson(message, Appointment.class);
        appointment.save();
 }

 @Entity
 public class Appointment extends CalendarEvent {
       private String owner;
 }

 @MappedSuperclass
 public abstract class CalendarEvent extends Model {
       private long timestamp;
 }

EDIT: Added some code sample

Upvotes: 3

Views: 11079

Answers (2)

siamii
siamii

Reputation: 24114

I've found this workaround to work for now. Maybe it's to do with how Gson and Jpa interact. Not sure...

public void processMessage(String message) {
        Appointment appointment = new Gson().fromJson(message, Appointment.class);
        //recreate appointment
        appointment = new Appointment(appointment);
        appointment.save();  }

Upvotes: 0

millhouse
millhouse

Reputation: 10007

Basically it means the EntityManager won't track them any more: This is a good overview. How they come to be considered detached when you didn't explicitly ask for it is odd. But the workaround might be to use a "defensive" approach in your DAO method and merge these odd cases; i.e.:

public void save (Model possiblyDetachedModel) {

   if (entityManager.contains(possiblyDetachedModel)) {
        entityManager.merge(possiblyDetachedModel);
    } else {
        entityManager.persist(possiblyDetachedModel);
    }
}

I don't particularly like it, because it shouldn't need to be done when you're persisting brand-new objects. It might be worth putting some logging in (or debugging if at all possible) the "merge" branch and really inspecting those rogue objects - I'm pretty sure the EntityManager can only use the @Id field as the detection mechanism...

Upvotes: 5

Related Questions