I have the following @Transactional
method that's managed by a HibernateTransactionManager
:
@Transactional(rollbackFor = NoActiveTraceException.class)
public void insertTraceEvent(TraceEvent traceEvent) throws NoActiveTraceException {
Trace trace = traceDao.findActiveForNumber(traceEvent.getSourceParty());
if (trace == null) {
throw new NoActiveTraceException(traceEvent.getSourceParty()); // custom
} else {
traceEvent.setTrace(trace);
trace.getTraceEvents().add(traceEvent); // returns set to which I can add
// there is a Cascade.All on the @OneToMany set
traceDao.create(traceEvent);
}
}
// ...in traceDao
@Override
public Long create(TraceEvent traceEvent) {
getCurrentSession().persist(traceEvent);
return traceEvent.getTraceEventId();
}
Trace
and TraceEvent
are entities managed by Hibernate.
When I tested this code during development, I never had any problems. In production though, from time to time, it'll throw
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [myobject#2664]
at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:136) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:208) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:151) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:78) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:843) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:818) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:822) ~[hibernate-core-4.1.9.Final.jar:4.1.9.Final]
at com.package.model.dao.impl.TraceDaoHibernateImpl.create(TraceDaoHibernateImpl.java:116) ~[TraceDaoHibernateImpl.class:na]
when trying to persist it. I can't think of any difference between prod and dev that would cause this. This procedure is independent of everything (and runs within a transaction).
I understand that once I call .add(traceEvent)
, it dirties the PersistentSet
(and might persist
the traceEvent). And then when I call create(traceEvent)
, it might try to persist
it as well, thereby adding the same object in the Hibernate Session.
Why is the exception occurring and why is it happening at random intervals like this?
Edit Entities
@Entity
@Table(name = "TRACE_EVENT")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class TraceEvent {
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(name = "trace_event_id")
private Long traceEventId;
@Column(nullable = false)
private Calendar transmissionDate;
@Column(nullable = false)
private String sourceParty;
@Column
private String destinationParty;
@ManyToOne(optional = false)
@JoinColumn(name = "trace_id")
private Trace trace;
And
@Entity
@Table(name = "TRACE")
public class Trace {
@Id
@GenericGenerator(name = "generator", strategy = "increment")
@GeneratedValue(generator = "generator")
@Column(name = "trace_id")
private Long traceId;
@Column(nullable = false)
private String number;
@Column(nullable = false)
private Calendar startDate;
@Column
private Calendar endDate;
@Column(nullable = false)
private Boolean isActive = false; // default value of false
// USED TO BE CASCADE.ALL
@OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.REMOVE}, mappedBy = "trace")
private Set<TraceEvent> traceEvents = new HashSet<>();
@ManyToOne
private Account account;