2015-05-26 39 views
13

使用Spring Data REST。如果您有oneToMany或ManyToOne關係,則PUT操作在「非擁有」實體上返回200,但實際上並未持續連接的資源。如何與Spring Data REST和JPA保持雙向關係?

示例實體。

@Entity(name = 'author') 
@ToString 
class AuthorEntity implements Author { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    Long id 

    String fullName 

    @ManyToMany(mappedBy = 'authors') 
    Set<BookEntity> books 
} 


@Entity(name = 'book') 
@EqualsAndHashCode 
class BookEntity implements Book { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    Long id 

    @Column(nullable = false) 
    String title 

    @Column(nullable = false) 
    String isbn 

    @Column(nullable = false) 
    String publisher 

    @ManyToMany(fetch = FetchType.LAZY, cascade = [CascadeType.ALL]) 
    Set<AuthorEntity> authors 
} 

如果您有PagingAndSortingRepository支持他們,你可以得到一本書,跟隨作者的書鏈接,做一個PUT與URI一個作者與相關聯的。你不能走另一條路。

如果你做一個作者一個GET,做一個穿上它的圖書鏈接,響應返回200,但關係永遠不會持久。

這是預期的行爲?

+2

雙向關聯必須手動維護在對象模型中,通常在setters內​​完成。 Spring Data REST默認使用字段訪問。這意味着協會的變化不會在定義的協會的另一面反映出來。你有沒有試過,在'@ PrePersist' /'@ PreUpdate'方法中觸發同步?或切換到財產訪問? –

+1

我也投票解決雙向關聯。一個'Author'可以存在,沒有'Book'。可以使用存儲庫方法'設置 BookRepository.findByAuthor(作者作者)'來檢索'作者'的書籍。這樣可以簡化模型,因爲您不必手動維護引用。 –

+0

我可以看到一個庫界面,您需要找到作者,然後查看它的書籍。如果沒有雙向關聯,這將不被支持。不是管理實體關聯的粉絲,如果它不是DATA REST的意圖。感謝您的參與,對書籍作者進行過濾似乎是最好的方法。不要在任何可能的情況下保持單向。 :) – keaplogik

回答

24

TL;博士

的關鍵,不在春季數據REST這麼多的東西 - 因爲你可以很容易地得到它在方案中的工作 - 但要確保你的模型保持協會的兩端同步。

問題

你在這裏看到的問題的事實,春季數據REST基本修改您的AuthorEntitybooks屬性出現。這本身並不反映BookEntityauthors屬性中的此更新。這需要手動解決,這不是Spring Data REST所構成的限制,而是JPA通常工作的方式。只需手動調用設置器並嘗試保留結果,就可以重現錯誤的行爲。

如何解決這個問題?

如果刪除雙向關聯是不是一種選擇(見下文爲什麼我建議這個),使這項工作的唯一方法是使該協會確定的變化在兩側體現。通常人們通過在添加了一本書手動添加筆者以BookEntity照顧這一點:

class AuthorEntity { 

    void add(BookEntity book) { 

    this.books.add(book); 

    if (!book.getAuthors().contains(this)) { 
     book.add(this); 
    } 
    } 
} 

如果子句會一直要在BookEntity側增加,以及如果你想確保附加另一方面的變化也會傳播。基本上需要if,否則這兩種方法會不斷地自行調用。

春季數據REST,默認使用字段訪問,這樣實際上世界上沒有其他方法,你可以把這個邏輯之中。一種選擇是切換到財產訪問並將邏輯放入設置者。另一種選擇是使用註釋爲​​/@PrePersist的方法來迭代實體,並確保修改反映在兩邊。

拆除問題

正如你所看到的根本原因,這增加了不少複雜的域模型。

#雙向協會1規則:正如我在Twitter上開玩笑說昨天沒有使用這些... :)

通常,如果你儘量不要使用雙向簡化了此事只要有可能,就退回到一個存儲庫,以獲得組成協會背後的所有實體。

一個很好的啓發式方法可以確定哪一邊是關聯關係對於你建模的領域真正的核心和關鍵。在你的情況下,我認爲,對於作者來說,沒有她寫的書是完美的。另一方面,沒有作者的書根本沒有太多意義。所以我想保持authors財產BookEntity但引進的BookRepository下面的方法:

interface BookRepository extends Repository<Book, Long> { 

    List<Book> findByAuthor(Author author); 
} 

是的,這需要以前可能只是援引author.getBooks()所有客戶端現已與倉庫工作。但從積極的一面來看,你已經從你的域對象中刪除了所有的東西,並創建了一個明確的從書到作者的依賴方向。書籍取決於作者,而不是相反。

+4

您的答案的「模板」應該以某種方式標準化爲SO:P!好東西! – geoand

+0

如果我很好地讀到你,你建議刪除所有OneToMany關係,並只保留他們的ManyToOne吊墜? 這將是我對實體進行建模的真正重大改變...... –

+0

不,我不推薦任何基於附註的基礎。我建議不要使用雙向關聯。實際上,我甚至沒有提到任何註釋,所以我不完全明白你是如何得出這種解釋的。您削減的協會的哪一方很大程度上取決於用例。請重新閱讀提及啓發式的部分進行論證。 –

1

我遇到了一個類似的問題,雖然通過REST API將我的POJO(包含雙向映射@OneToMany和@ManyToOne)作爲JSON發送,但數據在父實體和子實體中都保留,但外鍵關係不是成立。發生這種情況是因爲需要手動維護雙向關聯。

JPA提供了一個註釋@PrePersist,它可以用來確保在實體持久化之前執行用它註釋的方法。由於JPA首先將父實體插入到子實體後面的數據庫中,我添加了一個註釋爲@PrePersist的方法,該方法將迭代子實體列表並手動將父實體設置爲它。

在你的情況下,它會是這樣的:

class AuthorEntitiy { 
    @PrePersist 
    public void populateBooks { 
     for(BookEntity book : books) 
      book.addToAuthorList(this); 
    } 
} 

class BookEntity { 
    @PrePersist 
    public void populateAuthors { 
     for(AuthorEntity author : authors) 
      author.addToBookList(this); 
    } 
} 

此之後,你可能會得到一個無限遞歸誤差,避免註釋父類@JsonManagedReference@JsonBackReference你的子類。這個解決方案爲我工作,希望它也適用於你。

相關問題