2016-09-28 136 views
2

所以我已經看過有關Spring數據的JPA的各種教程,這在許多場合已經完成了不同的工作,我不確定正確的方法是什麼。如何更好地更新Spring數據中的JPA實體?

假設存在follwing實體:

package stackoverflowTest.dao; 

import javax.persistence.*; 

@Entity 
@Table(name = "customers") 
public class Customer { 

@Id 
@GeneratedValue(strategy = GenerationType.AUTO) 
@Column(name = "id") 
private long id; 

@Column(name = "name") 
private String name; 

public Customer(String name) { 
    this.name = name; 
} 

public Customer() { 
} 

public long getId() { 
    return id; 
} 

public String getName() { 
    return name; 
} 

public void setName(String name) { 
    this.name = name; 
} 
} 

我們也有其在服務層檢索,然後交給控制器/客戶端側的DTO。

package stackoverflowTest.dto; 

public class CustomerDto { 

private long id; 
private String name; 

public CustomerDto(long id, String name) { 
    this.id = id; 
    this.name = name; 
} 

public long getId() { 
    return id; 
} 

public void setId(long id) { 
    this.id = id; 
} 

public String getName() { 
    return name; 
} 

public void setName(String name) { 
    this.name = name; 
} 
} 

所以現在假設客戶希望在WebUI改變了他的名字 - 然後會有一些控制器動作,哪裏就會有更新的DTO與舊的標識,新的名稱。

現在我必須將此更新的DTO保存到數據庫。

不幸目前還沒有辦法更新現有的客戶(除非不是刪除DB中的條目,並創建一個新的自動生成的ID的新Cusomter)

然而,由於這是不可行的(特別是考慮這樣的實體可能有數百個關係潛在的) - 所以來2直截了當的解決方案,我的腦海裏:

  1. 做二傳手在客戶類的ID - 從而允許ID的設置,然後保存客戶對象通過相應的存儲庫。

  • id字段添加到構造,只要你想更新的客戶,您始終創建與老ID的新對象,但新價值爲其他領域(在這種情況下只有名稱)
  • 所以我的問題是否有一個通用的規則如何做到這一點? 任何可能是什麼我解釋的兩種方法的缺點是?

    +1

    「因此,目前沒有辦法更新現有客戶(除了刪除數據庫中的條目並使用新的自動生成的ID創建新的Cusomter)」[說...什麼?](http:///www.objectdb.com/java/jpa/persistence/update)。你是如何得到這個想法的? –

    +0

    如果你使用相同的ID調用'save()',而你沒有重寫它,它會給你一個重複的ID異常。如果您已經正確配置了Spring事務處理,那麼無論何時從數據庫中提取客戶並簡單地設置所需的值,都可以看到有些查詢將寫入日誌中。但仍然讓我們考慮它沒有JPA,在你的情況下,通常如何用簡單的SQL更新客戶行?如果你沒有ID? – AntJavaDev

    +0

    我會在一會兒添加一些分類,感謝您的意見 –

    回答

    13

    更妙然後@Tanjim拉赫曼回答您可以使用Spring數據JPA使用的方法T getOne(ID id)

    Customer customerToUpdate = customerRepository.getOne(id); 
    customerToUpdate.setName(customerDto.getName); 
    customerRepository.save(customerToUpdate); 
    

    是的更好,因爲getOne(ID id)gets you only a reference(代理)對象,並不從數據庫中獲取它。在這個參考上,你可以設置你想要的和save()它只會執行一個像你期望的SQL UPDATE語句。在比較中,當您撥打find()時,就像在@Tanjim Rahmans中那樣回答Spring數據JPA將在您剛剛更新時執行SQL SELECT以從DB中實體獲取實體,而您不需要這些實體。

    +0

    ,我已經檢查了你的答案。由於沒有從數據庫中讀取數據,它肯定會比查找性能好。但它也有安全問題。如果您不想更新客戶表的一列,該怎麼辦?想象一下,你想更新客戶名稱,但你不想更新客戶創建日期(因爲它只會通過插入,而不會更新)。在這些情況下,更新之前取出DB當前行會更好。另外,如果您想使用JPA版本進行樂觀鎖定,則可能會遇到問題。 – Esty

    1

    這是一個比jpa問題更多的對象初始化問題,這兩種方法都可以工作,並且您可以同時使用它們,通常情況下,如果在使用構造函數參數的實例化之前數據成員值已準備好,if這個值可以在實例化後更新,你應該有一個setter。

    +0

    您是對的,但是我想知道是否有一些特殊的最佳實踐使用JPA –

    +0

    Hibernate是一個集合API可以讓你使用java模型而不是sql,你需要準備好你的模型,這樣你就可以使用這些API的 –

    +0

    是的,我知道這一點,但我想知道這種方法還有一些缺點,或者甚至一些隱藏的陷阱。 –

    1

    如果您需要直接使用DTO而不是實體,那麼您應該檢索現有的 Customer實例,並將DTO中的更新字段映射到該實例。

    Customer entity = //load from DB 
    //map fields from DTO to entity 
    
    +0

    的更多細節答案,因此您建議從DB加載實體,然後通過setters更新字段? –

    +0

    只有一個問題: 所以你會說,對數據庫做另一個查詢的開銷通常可以忽略不計? –

    2

    簡單JPA更新..

    Customer customer = em.find(id, Customer.class); //Consider em as JPA EntityManager 
    customer.setName(customerDto.getName); 
    em.merge(customer); 
    
    +0

    em.getReference會執行得更好,因爲它只提取羅伯特·涅斯特羅伊和盧卡斯·馬科爾的編號 – Journeycorner