我有以下由@Transactional
管理的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
和TraceEvent
是由Hibernate管理的实体。
当我在开发期间测试此代码时,我从未遇到任何问题。但是在生产中,它会不时地抛出
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]
试图坚持下去。我想不出会导致这种情况的prod和dev之间的任何区别。此过程独立于所有事务(并在事务中运行)。
我理解,一旦我调用.add(traceEvent)
,它就会弄脏PersistentSet
(并且可能persist
traceEvent。然后当我调用create(traceEvent)
时,它也可以尝试persist
,从而在Hibernate Session中添加相同的对象。
为什么会发生异常,为什么会以这样的随机间隔发生?
编辑实体
@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;
并且
@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;
答案 0 :(得分:1)
自从我上次使用Hibernate以来已经有一段时间了,这个问题很难解决,所以我只想描述各种选项:
由于您的Trace
对象有一个Cascade.All
选项,仅仅在集合上添加TraceEvent
并调用session.save(trace)
是不够的?就像现在一样,我的印象是,当会话关闭时,它会尝试保存Trace
对象。或者避免修改集合。只需保存TraceEvent
即可。您不会返回Trace
以供使用,因此修改它没有意义。下次使用时,它可能会从数据库中加载。以上所有行为可能取决于您的配置,但值得一看。
你使用某种休眠缓存吗?可能是Trace
对象可能不是最新版本吗?
如何创建TraceEvent
?可能是这个对象是分离的(在先前的请求中加载并现在重新使用?)似乎不太可能,但请检查。
这似乎是一个可以从很多地方调用的函数(即记录用户的动作)。是否有可能其中一个地方正在使用相同的会话并可能修改它?
假设它是从很多地方调用的,您是否可以从日志中将错误与某些特定的业务功能相关联(例如,当用户单击该链接/按钮等时)?
如果所有其他方法都失败了,为什么不在下次更新生产环境时尝试添加EventListener
或Interceptor
on persist事件(我知道您无法复制此内容)很容易出错)。这将是一个简单的抓住NonUniqueObjectException
它可以打印额外的日志信息,然后只是重新抛出