У меня есть транзакция, которая сохраняет или обновляет набор объектов в базе данных.Это код:
@Transactional
public void updatePDBEntry(Set<PDBEntry> pdbEntrySet) {
for (PDBEntry pdbEntry : pdbEntrySet) {
PDBEntry existingEntry = findByAccessionCode(pdbEntry.getAccessionCode());
if (existingEntry != null) {
log.debug("Remove previous version of PDBEntry {}", existingEntry);
makeTransient(existingEntry);
}
makePersistent(pdbEntry);
}
}
И в genericDAO:
public void makePersistent(I entity) {
getCurrentSession().saveOrUpdate(entity);
}
public void makeTransient(I entity) {
getCurrentSession().delete(entity);
}
Почему-то это не работает, он говорит, что не может вставить объект из-за дублированного ключа,хотя я могу видеть в журналах, что он получает makeTransient ().Я предполагаю, что это связано с тем, что все это происходит внутри транзакции, поэтому изменения, сделанные makeTransient (), могут быть не видны методом makePersistent ().Я мог бы решить эту проблему, скопировав все данные из pdbEntry в существующийEntry, а затем выполнив saveOrUpdate (существующийEntry), но это своего рода грязный хак.Есть ли другой способ убедиться, что makeTransient видим для makePersistent, при этом сохраняя все это внутри транзакции?
EDIT: Это моя модель домена PDBEntry:
@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false, of = { "accessionCode", "date" })
@SuppressWarnings("PMD.UnusedPrivateField")
public class PDBEntry extends DomainObject implements Serializable {
@NaturalId
@NotEmpty
@Length(max = 4)
private String accessionCode;
@NaturalId
@NotNull
@Temporal(TemporalType.DATE)
private Date date;
private String header;
private Boolean isValidDssp;
@Temporal(TemporalType.TIMESTAMP)
private Date lastUpdated = new Date(System.currentTimeMillis());
@OneToOne(mappedBy = "pdbEntry", cascade = CascadeType.ALL)
private ExpMethod expMethod;
@OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Refinement> refinementSet = new HashSet<Refinement>();
@OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<HetGroup> hetGroupSet = new HashSet<HetGroup>();
@OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Chain> chainSet = new HashSet<Chain>();
@OneToMany(mappedBy = "pdbEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Chain> residueSet = new HashSet<Chain>();
public Date getLastUpdated() {
return new Date(lastUpdated.getTime());
}
public void setLastUpdated() throws InvocationTargetException {
throw new InvocationTargetException(new Throwable());
}
public void touch() {
lastUpdated = new Date(System.currentTimeMillis());
}
@Override
public String toString() {
return accessionCode;
}
public PDBEntry(String accessionCode, Date date) throws NullPointerException {
if (accessionCode != null && date != null) {
this.accessionCode = accessionCode;
this.date = date;
} else {
throw new NullPointerException();
}
}
}
@MappedSuperclass
public abstract class DomainObject implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
public Long getId() {
return id;
}
@Override
public abstract boolean equals(Object obj);
@Override
public abstract int hashCode();
@Override
public abstract String toString();
}
EDIT: Newпроблема, я создал метод, который сначала удаляет все существующие объекты из базы данных следующим образом:
@Override
@Transactional
public void updatePDBEntries(Set<PDBEntry> pdbEntrySet) {
findAndRemoveExistingPDBEntries(pdbEntrySet);
savePDBEntries(pdbEntrySet);
}
@Override
@Transactional
public void findAndRemoveExistingPDBEntries(Set<PDBEntry> pdbEntrySet) {
for (PDBEntry pdbEntry : pdbEntrySet) {
PDBEntry existingPDBEntry = findByAccessionCode(pdbEntry.getAccessionCode());
if (existingPDBEntry != null) {
log.info("Delete: {}", pdbEntry);
makeTransient(existingPDBEntry);
}
}
}
@Override
@Transactional
public void savePDBEntries(Set<PDBEntry> pdbEntrySet) {
for (PDBEntry pdbEntry : pdbEntrySet) {
log.info("Save: {}", pdbEntry);
makePersistent(pdbEntry);
}
}
Кажется, что удаляет первые 73 записи, которые он встречает, но затем выдает ошибку:
WARN 2010-10-25 14:28:49,406 main JDBCExceptionReporter:100 - SQL Error: 0, SQLState: 23503
ERROR 2010-10-25 14:28:49,406 main JDBCExceptionReporter:101 - Batch entry 0 /* delete nl.ru.cmbi.pdbeter.core.model.domain.PDBEntry */ delete from PDBEntry where id='74' was aborted. Call getNextException to see the cause.
WARN 2010-10-25 14:28:49,406 main JDBCExceptionReporter:100 - SQL Error: 0, SQLState: 23503
ERROR 2010-10-25 14:28:49,406 main JDBCExceptionReporter:101 - ERROR: update or delete on table "pdbentry" violates foreign key constraint "fke03a2dc84d44e296" on table "hetgroup"
Detail: Key (id)=(74) is still referenced from table "hetgroup".
ERROR 2010-10-25 14:28:49,408 main AbstractFlushingEventListener:324 - Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
Есть идеи, как эта ошибка может возникнуть?
РЕДАКТИРОВАТЬ: Я обнаружил проблему: последняя ошибка была вызвана моделью гетерогенной группы, которая выглядит следующим образом:
@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EqualsAndHashCode(callSuper = false, of = { "pdbEntry", "hetId" })
@SuppressWarnings("PMD.UnusedPrivateField")
// extends DomainObject which contains Id, NaturalId is not enough in this case, since duplicate chains still exist
// in fact this is an error of the PDBFinder and will be fixed in the future
public class HetGroup extends DomainObject implements Serializable {
//@NaturalId
@NotNull
@ManyToOne
private PDBEntry pdbEntry;
//@NaturalId
@NotEmpty
private String hetId;
private Integer nAtom;
@Length(max = 8192)
private String name;
public HetGroup(PDBEntry pdbEntry, String hetId) {
this.pdbEntry = pdbEntry;
pdbEntry.getHetGroupSet().add(this);
this.hetId = hetId;
}
}
Особенно обратите внимание на комментарии @ NaturalId, я прокомментировал их, потому что в моих данных все еще была некоторая ошибка, которая вызвала дублирование гетерогенных групп, поэтому я решил, что на данный момент я просто удалил для них ограничение UNIQUE, но я забыл, что используюЛомбок для создания методов равенства и хэш-кода для меня, как видно из строки:
@EqualsAndHashCode(callSuper = false, of = { "pdbEntry", "hetId" })
Это вызвало ошибку с дублирующимися HetId.Я исправил данные и восстановил @ NaturalId, и теперь все работает нормально.
Спасибо всем!