У меня есть две сущности, которые связаны через отношение OneToMany:
@Entity
@Table(name="bookcase")
public class BookCase {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Transient
@Getter @Setter private Long oldId;
/*
https://vladmihalcea.com/a-beginners-guide-to-jpa-and-hibernate-cascade-types/
*/
@OneToMany(mappedBy = "bookCase", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Bookshelf> bookShelves = new HashSet<>();
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Set<Bookshelf> getBookShelves() { return bookShelves; }
public void setBookShelves(Set<Bookshelf> bookShelves) { this.bookShelves = bookShelves; }
}
@Entity
@Table(name="bookshelf")
public class Bookshelf {
private static final Logger log = LoggerFactory.getLogger(Bookshelf.class);
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Transient
@Getter @Setter private Long oldId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "bookcase_id")
private BookCase bookCase;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public BookCase getBookCase() { return bookCase; }
public void setBookCase(BookCase bookCase) {
this.bookCase = bookCase;
bookCase.getBookShelves().add(this);
}
@Transient
@Setter private OldIdListener oldIdListener;
/*
When the id is saved, listening DTOs can update their ids
*/
@PostPersist
public void triggerOldId() {
log.info("Postpersist triggered for {}", id);
if (oldIdListener != null) {
oldIdListener.updateId(oldId, id);
}
}
}
public interface OldIdListener {
void updateId(long oldId, long newId);
}
Следующий тест не пройден:
@Test
public void testThatCascadingListenerIsTriggered() {
var mock = mock(OldIdListener.class);
var mock2 = mock(OldIdListener.class);
var mock3 = mock(OldIdListener.class);
var bookcase = new BookCase();
var shelf1 = new Bookshelf();
shelf1.setOldId(-5L);
shelf1.setBookCase(bookcase);
shelf1.setOldIdListener(mock);
var shelf2 = new Bookshelf();
shelf2.setOldId(-6L);
shelf2.setBookCase(bookcase);
shelf2.setOldIdListener(mock2);
var saved = bookCaseRepository.save(bookcase);
verify(mock).updateId(eq(-5L), anyLong());
verify(mock2).updateId(eq(-6L), anyLong());
var savedBookCase = bookCaseRepository.findById(saved.getId()).get();
assertThat(savedBookCase.getBookShelves()).hasSize(2);
var shelf3 = new Bookshelf();
shelf3.setOldId(-10L);
shelf3.setBookCase(savedBookCase);
shelf3.setOldIdListener(mock3);
savedBookCase.getBookShelves().add(shelf3);
bookCaseRepository.save(savedBookCase);
verify(mock3).updateId(eq(-10L), anyLong());
}
mock3 никогда не вызывается.
При отладке кода я вижу, что переходные поля oldId
и oldIdListener
устанавливаются в нулевое значение, когда метод @PostPersist
вызывается для объекта shelf3, а не для shelf1 и 2.
Я думаю, это потому, что я модифицирую объект Set; но объект правильно сохраняется, он просто теряет все переходные поля. Этого не происходит, когда все дерево сохраняется впервые.
Это неправильный способ вставки нового элемента в набор OneToMany или где здесь ошибка?
Я использую Spring Boot 2.1.
Спасибо!