簡體   English   中英

事務結束時自動刷新的例外

[英]Exception on automatic flush at the end of a transaction

我正在用一個條目填充數據庫,如果已經存在則應替換。 為此,首先,我檢查該條目是否已經存在一個查找(包裝在只讀事務中),然后如果返回的值為null,則我將新創建的條目(包裝在事務中)持久化,如果返回值是一個現有條目,我首先將其刪除(包裝在事務中),然后添加新條目。 除了這些很多事務可以在單個事務中完成的事實之外,我想知道為什么如果條目已經存在,為什么會出現異常。 我已經檢查了刷新模式,並將其設置為AUTO,因此它應在事務結束時進行刷新。 這是異常的堆棧跟蹤:

Exception in thread "main" java.lang.NullPointerException
    at org.hibernate.engine.internal.NaturalIdXrefDelegate.validateNaturalId(NaturalIdXrefDelegate.java:175)
    at org.hibernate.engine.internal.NaturalIdXrefDelegate.cacheNaturalIdCrossReference(NaturalIdXrefDelegate.java:85)
    at org.hibernate.engine.internal.StatefulPersistenceContext$1.cacheNaturalIdCrossReferenceFromLoad(StatefulPersistenceContext.java:1817)
    at org.hibernate.engine.internal.StatefulPersistenceContext.getNaturalIdSnapshot(StatefulPersistenceContext.java:340)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.checkNaturalId(DefaultFlushEntityEventListener.java:110)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:199)
    at org.hibernate.event.internal.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:156)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:225)
    at org.hibernate.event.internal.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
    at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1213)
    at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:402)
    at org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction.beforeTransactionCommit(JdbcTransaction.java:101)
    at org.hibernate.engine.transaction.spi.AbstractTransactionImpl.commit(AbstractTransactionImpl.java:175)
    at org.springframework.orm.hibernate4.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:480)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:392)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy31.save(Unknown Source)
    at controller.FileParserCore.update(FileParserCore.java:39)
    at controller.Core.runCore(Core.java:122)
    at controller.Core.staticRunCore(Core.java:74)
    at controller.Core.main(Core.java:33)

我正在使用Hibernate和Spring。

如果條目已經存在,這是失敗的代碼位:

Entry entry = getNewEntry();
if (entryDAO.find(entry.getName()) != null) {
    entryDAO.remove(entry.getName());
}
entryDAO.save(entry);

這是相應的DAO:

@Repository
public class EntryDAO extends GenericDAO<Entry> implements IEntryDAO {
    private static final Logger log = LoggerFactory.getLogger(EntryDAO.class);

    @Override
    @Transactional
    public void save(Entry entry) {
        makePersistent(entry);
        log.info("Saved: {}", entry);
    }

    @Override
    @Transactional
    public void remove(String name) {
        Entry entry = find(name);
        if (entry != null) {
            makeTransient(entry);
            log.info("Removed: {}", entry);
        } else {
            log.warn("Could not remove: {}, entry not found", name);
        }
    }

    /**
     * find an entry by its name and return it
     */
    @Override
    @Transactional(readOnly = true)
    public Entry find(String name) {
        return (Entry) createCriteria(Restrictions.eq("name",name)).uniqueResult();
    }
}

這是Entry域對象:

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false, of = { "name" })
public class Entry extends DomainObject implements Serializable {
    @NaturalId
    @NotEmpty
    @Length(max = 16)
    @Index(name = "ix_name")
    private String name;
}

這是直到異常的輸出:

INFO  2013-03-01 10:22:17,899 main - Removed: someEntry
INFO  2013-03-01 10:22:18,117 main - Saved: someEntry

當我檢查數據庫時,將刪除舊條目,但不會保存新條目。 此外,舊條目和新條目的ID不同,只是名稱相同。 從stacktrace看來,它似乎與naturalID有關,也許是因為刷新未完成刪除舊條目的原因,新條目的名稱與此沖突嗎? 但是,當舊條目進入save()方法時,難道不應該早已消失嗎?

更新:我還檢查了將所有內容放入單個事務中是否可行,但是當我嘗試刪除條目並隨后立即添加具有相同NaturalID(其名稱)的條目時,它當然開始哭了。 所以我想:讓我們在它們之間放置一個getCurrentSession()。flush()。 結果:與上述相同的異常。

更新:這是DomainObject類:

@MappedSuperclass
@EqualsAndHashCode
public abstract class DomainObject implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long    id;

    public Long getId() {
        return id;
    }

    @Override
    public abstract String toString();
}

抱歉,我已經解決了。 這與此處未提及的多對多關系中的意外刪除級聯有關(我之所以沒有提及,是因為我認為這與問題無關)。 原來是一次刪除就刪除了所有內容,因為多個條目是通過多對多級聯相互鏈接的。 除去級聯后,所有問題均得到解決。

就我而言,我有一個由EntityListener引起的休眠操作嵌套。 在某個實體的保存操作期間,將調用審核攔截器(偵聽器),該監聽器將從數據庫讀取內容,從而使會話混亂。

解決方案是為攔截器打開一個新的會話和事務。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM