简体   繁体   English

事务结束时自动刷新的例外

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

I'm filling a database with entries that should be replaced if they are already present. 我正在用一个条目填充数据库,如果已经存在则应替换。 To do this, first I check if the entry is already present with a find (which is wrapped in a read only transaction), then if the returned value is null, I persist the newly made entry (wrapped in a transaction), if the returned value is an existing entry, I first remove it (wrapped in a transaction) and then add the new entry. 为此,首先,我检查该条目是否已经存在一个查找(包装在只读事务中),然后如果返回的值为null,则我将新创建的条目(包装在事务中)持久化,如果返回值是一个现有条目,我首先将其删除(包装在事务中),然后添加新条目。 Besides the fact that these are a lot of transactions that could be done inside a single transaction, I'm wondering why I get an exception if an entry is already present. 除了这些很多事务可以在单个事务中完成的事实之外,我想知道为什么如果条目已经存在,为什么会出现异常。 I've checked the flush mode and it's set to AUTO, so it should be flushing at the end of a transaction. 我已经检查了刷新模式,并将其设置为AUTO,因此它应在事务结束时进行刷新。 This is the stacktrace of the exception: 这是异常的堆栈跟踪:

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)

I'm using Hibernate and Spring. 我正在使用Hibernate和Spring。

This is the bit of code that fails if an entry is already present: 如果条目已经存在,这是失败的代码位:

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

This is the corresponding DAO: 这是相应的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();
    }
}

And this is the Entry domain object: 这是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;
}

This is the output up until the exception: 这是直到异常的输出:

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

When I check the database, the old entry is removed, but the new entry is not saved. 当我检查数据库时,将删除旧条目,但不会保存新条目。 Also, the IDs of the old entry and the new entry are different, just the names are the same. 此外,旧条目和新条目的ID不同,只是名称相同。 From the stacktrace it seems like it has something to do with the naturalID, maybe because the flush didn't finish removing the old entry the name of the new entry is clashing with this? 从stacktrace看来,它似乎与naturalID有关,也许是因为刷新未完成删除旧条目的原因,新条目的名称与此冲突吗? But shouldn't the old entry be long gone already when it enters the save() method? 但是,当旧条目进入save()方法时,难道不应该早已消失吗?

UPDATE: I also checked whether putting everything within a single transaction would work, but of course it started crying when I tried removing an entry and immediately afterwards adding an entry with the same NaturalID (their name). 更新:我还检查了将所有内容放入单个事务中是否可行,但是当我尝试删除条目并随后立即添加具有相同NaturalID(其名称)的条目时,它当然开始哭了。 So I thought: let's put a getCurrentSession().flush() inbetween. 所以我想:让我们在它们之间放置一个getCurrentSession()。flush()。 Result: the same Exception as above. 结果:与上述相同的异常。

UPDATE: Here's the DomainObject class: 更新:这是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();
}

I'm sorry but I've solved it already. 抱歉,我已经解决了。 It had to do with an accidental cascading of removal in a many-to-many relation not mentioned here (I didn't mention it because I thought it wasn't related to the problem). 这与此处未提及的多对多关系中的意外删除级联有关(我之所以没有提及,是因为我认为这与问题无关)。 Turns out it was removing everything with one single removal because multiple entries were linked with each other through the many-to-many cascading. 原来是一次删除就删除了所有内容,因为多个条目是通过多对多级联相互链接的。 After removing the cascade all problems were solved. 除去级联后,所有问题均得到解决。

In my case I had a nesting of hibernate operations caused by an EntityListener. 就我而言,我有一个由EntityListener引起的休眠操作嵌套。 During the save operation of some entity, an audit interceptor (listener) would be called and would read stuff from the DB, messing up the session. 在某个实体的保存操作期间,将调用审核拦截器(侦听器),该监听器将从数据库读取内容,从而使会话混乱。

The solution was to open a new session and transaction for the interceptor. 解决方案是为拦截器打开一个新的会话和事务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM