我正在填充一个数据库,其中包含的条目如果已经存在则应予以替换。要做到这一点,首先我检查条目是否已经存在find(包含在只读事务中),然后如果返回的值为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不同,只是名称相同。从堆栈跟踪看起来它似乎与naturalID有关,也许是因为flush没有完成删除旧条目新条目的名称与此冲突?但是当它进入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();
}
答案 0 :(得分:0)
我很抱歉,但我已经解决了。它与在这里没有提到的多对多关系中的意外级联删除有关(我没有提到它,因为我认为它与问题无关)。事实证明它删除了所有内容,只需一次删除,因为多个条目通过多对多级联相互链接。删除级联后,所有问题都解决了。
答案 1 :(得分:0)
在我的例子中,我有一个由EntityListener引起的hibernate操作嵌套。 在某个实体的保存操作期间,将调用审计拦截器(侦听器)并从数据库中读取内容,从而弄乱会话。
解决方案是为拦截器打开一个新的会话和事务。