Я заполняю базу данных записями, которые следует заменить, если они уже есть. Для этого сначала я проверяю, присутствует ли уже запись с помощью 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
Когда я проверяю базу данных, старая запись удаляется, но новая запись не сохраняется. Кроме того, идентификаторы старой записи и новой записи разные, только имена совпадают. Из трассировки стека кажется, что это как-то связано с 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();
}
DomainObject
неправильно установлен@Id
или отсутствует@MappedSuperclas
. Не могли бы вы опубликоватьDomainObject
? - person n1ckolas   schedule 01.03.2013