繁体   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