title: Exception on automatic flush at the end of a transaction
body: <p>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. 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. This is the stacktrace of the exception:</p>
  
  <pre><code>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)
  </code></pre>
  
  <p>I'm using Hibernate and Spring.</p>
  
  <p>This is the bit of code that fails if an entry is already present:</p>
  
  <pre><code>Entry entry = getNewEntry();
  if (entryDAO.find(entry.getName()) != null) {
      entryDAO.remove(entry.getName());
  }
  entryDAO.save(entry);
  </code></pre>
  
  <p>This is the corresponding DAO:</p>
  
  <pre><code>@Repository
  public class EntryDAO extends GenericDAO&lt;Entry&gt; 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();
      }
  }
  </code></pre>
  
  <p>And this is the Entry domain object:</p>
  
  <pre><code>@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;
  }
  </code></pre>
  
  <p>This is the output up until the exception:</p>
  
  <pre>
  INFO  2013-03-01 10:22:17,899 main - Removed: someEntry
  INFO  2013-03-01 10:22:18,117 main - Saved: someEntry
  </pre>
  
  <p>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. 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? But shouldn't the old entry be long gone already when it enters the save() method?</p>
  
  <p>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). So I thought: let's put a getCurrentSession().flush() inbetween. Result: the same Exception as above.</p>
  
  <p>UPDATE:
  Here's the DomainObject class:</p>
  
  <pre><code>@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();
  }
  </code></pre>