Java @事务性带注释的方法有时会引发非UniqueObjectException

Java @事务性带注释的方法有时会引发非UniqueObjectException,java,hibernate,transactions,Java,Hibernate,Transactions,我有以下@Transactional方法,该方法由HibernateTransactionManager管理: @Transactional(rollbackFor = NoActiveTraceException.class) public void insertTraceEvent(TraceEvent traceEvent) throws NoActiveTraceException { Trace trace = traceDao.findActiveForNumber(trac

我有以下
@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
(并且可能
persistent
traceEvent)。然后,当我调用
create(traceEvent)
时,它可能也会尝试
持久化它,从而在Hibernate会话中添加相同的对象

为什么会发生异常,为什么会以这样的随机间隔发生异常

编辑实体

@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;

@实体
@表(name=“TRACE”)
公共类跟踪{
@身份证
@GenericGenerator(name=“generator”,strategy=“increment”)
@GeneratedValue(generator=“generator”)
@列(name=“trace\u id”)
私有长traceId;
@列(nullable=false)
私有字符串编号;
@列(nullable=false)
私人日历开始日期;
@纵队
私人日历结束日期;
@列(nullable=false)
私有布尔值isActive=false;//默认值为false
//过去是瀑布式的
@OneToMany(fetch=FetchType.LAZY,cascade={CascadeType.REMOVE},mappedBy=“trace”)
私有集traceEvents=new HashSet();
@许多酮
私人帐户;

自从我上次使用Hibernate已经有一段时间了,这个问题很难解决,所以我只介绍各种选项:

  • 由于您的
    跟踪
    对象具有一个
    级联。所有
    选项仅在集合中添加
    跟踪事件
    并调用
    会话。保存(跟踪)就足够了
    ?现在,我的印象是,当会话关闭时,它将尝试保存
    跟踪
    对象。或者避免修改集合。只需保存
    跟踪事件
    。您不会返回任何要使用的
    跟踪
    ,因此修改它没有意义。下次使用它时,它可能会从数据库。所有上述行为可能取决于您的配置,但可能值得一看

  • 您是否使用某种hibernate缓存?是否可能是
    跟踪
    对象可能不是最新版本

  • TraceEvent
    是如何创建的?是否该对象已分离(在以前的请求中加载,现在重新使用?)这似乎不大可能,但请检查

  • 这似乎是一个可以从多个地方调用的函数(即,记录用户的操作)。是否有可能其他地方正在使用相同的会话并可能对其进行修改

  • 假设它是从许多地方调用的,您是否能够从日志中将错误与某些特定的业务功能联系起来(例如,当用户单击该链接/按钮等)

  • 如果所有其他操作都失败,为什么不尝试在下次更新生产环境时在持久化事件上添加一个
    EventListener
    拦截器
    (我知道您无法轻松复制此错误)。这将是一个简单的捕获
    ununiqueobjectexception
    的方法,它可以打印额外的日志信息,然后重新抛出


你检查过了吗?@c.s只查看了最上面的答案。这可能与级联有关,但我仍然不明白为什么会偶尔发生。你能发布有关实体的更多详细信息吗?hbm文件或注释类会很有用。@Joe Yup,发布了实体。底层SQL实现是什么,它是在一个cluste中r?我觉得这实际上不是级联问题,而是刷新问题。生成器策略可能不应该是递增的,而应该是表策略或本机策略。我将尝试您的第一点和最后一点。其他方面:我不使用Hibernate缓存。TraceEvent是以POJO样式创建的。这一切都发生在
@Transac中在计划执行器中发生的异常
块。我还将
Cascade.All
更改为
Cascade.Remove
@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;