附加新的OneToMany实体时,瞬变字段会丢失

时间:2019-01-04 14:51:47

标签: java spring spring-boot jpa spring-data-jpa

我有两个通过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而非架子1和2上调用oldId方法时,瞬变字段oldIdListener@PostPersist设置为null。

我认为这是因为我正在修改Set对象;但是该对象已正确保留,只会丢失所有瞬态字段。第一次保留整个树时,不会发生这种情况。

将新元素插入到OneToMany集中是错误的方法吗?还是这里的错误在哪里?

我正在使用Spring Boot 2.1。

谢谢!

1 个答案:

答案 0 :(得分:0)

使用@Transient注释的字段将不会保留在数据库中,因此,如果要保留它,则必须删除@Transient。