java - 附加新的 OneToMany 实体时,瞬态字段丢失
问题描述
我有两个通过 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 永远不会被调用。在调试代码时,我可以看到当在对象架子 3 上调用方法时,瞬态字段oldId
和oldIdListener
设置为空,而不是架子 1 和 2。@PostPersist
我认为这是因为我正在修改 Set 对象;但该对象被正确持久化,它只是丢失了所有瞬态字段。第一次持久化整个树时不会发生这种情况。
这是将新元素插入 OneToMany 集合的错误方法还是这里的错误在哪里?
我正在使用 Spring Boot 2.1。
谢谢!
解决方案
带有@Transient 注解的字段不会持久化到数据库中,所以如果要持久化,必须去掉@Transient。
推荐阅读
- kotlin - 在 Kotlin 中,var s: String = "hello" 和 var s = "hello" as String 有什么区别?
- bash - 在 Bash 脚本中并行使用 Ping
- virtualbox - Minikube 不能在 VirtualBox 上运行
- c# - 按 Enumerable 列表排序,没有给出想要的结果
- javascript - 用javascript取表中的数据
- sql - 对 SUM 没有信心,不确定如何限制字段
- c - 确定平均课程成绩的程序
- xcode - OSX 上的 Xcode 10 图像按钮状态
- python - 如何将一维数据转换为二维数组?
- java - TestNG - 如何在每次并行运行的类中的所有测试之前运行一次设置