2009-02-27 32 views

回答

25

我更喜歡訪問器,因爲我可以在需要時向我的訪問器添加一些業務邏輯。 下面是一個例子:

@Entity 
public class Person { 

    @Column("nickName") 
    public String getNickName(){ 
    if(this.name != null) return generateFunnyNick(this.name); 
    else return "John Doe"; 
    } 
} 

此外,如果你把另一個庫混進去(像一些JSON轉換lib或BeanMapper或推土機或基於的getter/setter屬性其他的bean映射/克隆LIB),你會保證lib與持久性管理器同步(都使用getter/setter)。

73

我更喜歡野外訪問,因爲這樣我不必爲每個屬性提供getter/setter。

通過Google進行的快速調查表明,大部分字段訪問權限(例如,http://java.dzone.com/tips/12-feb-jpa-20-why-accesstype)。

我相信字段訪問是Spring推薦的習語,但是我找不到一個引用來支持它。

有一個related SO question,試圖衡量表現,並得出結論,「沒有區別」。

+0

如果你不提供setter getter在實體然後什麼是該字段的使用...你不能在應用程序中使用它anwyhere,因爲字段是私人的 – anshulkatta 2014-07-03 10:49:41

+1

是不是提供一個getter和setter爲你的領域不好的做法?,我我猜這裏的評論並不總是正確的,因爲我正在承擔一個公共領域,而顯然它可能是一個永遠不會被訪問的私人領域。 – 2014-10-16 12:58:46

+2

@anshulkatta我覺得我應該真的解決你的問題,因爲封裝就是這樣。理想情況下,所有的字段都應該是私有的,如果可能的話,它們不應該有getter或setter - 這是您希望的最佳封裝級別。 考慮一個密碼檢查器。2個私人字段passwordHash和failedAttempts。兩者都可以是私人的,沒有吸氣劑或固色劑。它們由bool checkPassword(字符串密碼)使用,該密碼用於哈希值,對照passwordHash進行檢查,然後更新failedAttempts並返回結果。不需要其他代碼訪問這兩個字段。 – Martin 2015-09-29 14:11:11

6

我認爲,對於懶惰初始化,屬性訪問與字段訪問略有不同。

考慮2種基本豆以下映射:

<hibernate-mapping package="org.nkl.model" default-access="field"> 
    <class name="FieldBean" table="FIELD_BEAN"> 
    <id name="id"> 
     <generator class="sequence" /> 
    </id> 
    <property name="message" /> 
    </class> 
</hibernate-mapping> 

<hibernate-mapping package="org.nkl.model" default-access="property"> 
    <class name="PropBean" table="PROP_BEAN"> 
    <id name="id"> 
     <generator class="sequence" /> 
    </id> 
    <property name="message" /> 
    </class> 
</hibernate-mapping> 

而下面的單元測試:

@Test 
public void testFieldBean() { 
    Session session = sessionFactory.openSession(); 
    Transaction tx = session.beginTransaction(); 
    FieldBean fb = new FieldBean("field"); 
    Long id = (Long) session.save(fb); 
    tx.commit(); 
    session.close(); 

    session = sessionFactory.openSession(); 
    tx = session.beginTransaction(); 
    fb = (FieldBean) session.load(FieldBean.class, id); 
    System.out.println(fb.getId()); 
    tx.commit(); 
    session.close(); 
} 

@Test 
public void testPropBean() { 
    Session session = sessionFactory.openSession(); 
    Transaction tx = session.beginTransaction(); 
    PropBean pb = new PropBean("prop"); 
    Long id = (Long) session.save(pb); 
    tx.commit(); 
    session.close(); 

    session = sessionFactory.openSession(); 
    tx = session.beginTransaction(); 
    pb = (PropBean) session.load(PropBean.class, id); 
    System.out.println(pb.getId()); 
    tx.commit(); 
    session.close(); 
} 

你會看到在選擇所需的細微差別:

Hibernate: 
    call next value for hibernate_sequence 
Hibernate: 
    insert 
    into 
     FIELD_BEAN 
     (message, id) 
    values 
     (?, ?) 
Hibernate: 
    select 
     fieldbean0_.id as id1_0_, 
     fieldbean0_.message as message1_0_ 
    from 
     FIELD_BEAN fieldbean0_ 
    where 
     fieldbean0_.id=? 
0 
Hibernate: 
    call next value for hibernate_sequence 
Hibernate: 
    insert 
    into 
     PROP_BEAN 
     (message, id) 
    values 
     (?, ?) 
1 

也就是說,撥打fb.getId()需要選擇,而pb.getId()沒有。

+0

這很有趣! :)但是這是一個特定於實現的行爲,我敢肯定。我是 – 2009-02-27 18:45:59

+0

是的,我想這是由於只有持久類被檢測到的事實。 然而,這是一個可憐的地方,因爲id字段通常是一個沒有商業價值並且不需要任何訪問者的字段。 – 2009-03-25 11:23:56

13

這實際上取決於具體情況 - 兩種選項都可用是有原因的。國際海事組織歸結爲三種情況:

  1. setter有一些邏輯,不應該在從數據庫中加載實例時執行;例如,在setter中發生了一些值驗證,但是來自db的數據應該是有效的(否則它不會到達那裏(:);在這種情況下,字段訪問是最合適的; setter有一些邏輯應該總是即使在從db加載實例的過程中也是如此;例如,被初始化的屬性用於計算某個計算字段(例如屬性 - 金額,計算屬性 - 同一實例的幾個貨幣屬性的總和);在這種情況下,需要訪問屬性
  2. 以上情況都不存在 - 那麼這兩個選項都適用,只是保持一致(如果字段訪問是在這種情況下的選擇,那麼在類似情況下始終使用它) 。
+0

我是Hibernate的新手,正在努力解決同一個問題。我認爲這篇文章提供了最明確的答案。謝謝。 – 2012-06-25 05:22:04

0

通常豆類是POJO,所以它們有訪問者。

所以問題不是「哪一個更好?」,而只是「何時使用字段訪問?」。答案是「當你不需要場上的一個二傳手/吸引子!」時。

+4

問題是,您不能在POJO中混合字段訪問權限和屬性訪問權限 - 您必須選擇一個 – 2009-02-27 20:11:40

+0

真的嗎?我一定忘記了它。無論如何,我總是使用POJO和訪問器。 – 2009-03-01 02:37:52

7

我認爲註釋屬性更好,因爲更新字段直接破壞封裝,即使您的ORM執行了封裝。

下面是一個很好的例子,它會燒你:你可能希望你的註釋在休眠驗證器&持久性在同一個地方(字段或屬性)。如果你想測試一個在字段上註釋的hibernate validator動態驗證,你不能使用你的實體的模擬來將你的單元測試隔離到驗證器。哎喲。

0

我在想這個,我選擇方法accesor

爲什麼?

,因爲現場和methos accesor是一樣的 但如果以後我需要在負載領域中的一些邏輯,我保存移動放置在領域

問候

Grubhart

33

我傾向於選擇和使用屬性訪問:

  • 如果有需要,我可以添加邏輯(如接受的回答中提到)。
  • 它允許我在不初始化代理的情況下調用foo.getId()(在使用Hibernate時很重要,直到HHH-3718得到解決)。

缺點:

  • 它使代碼的可讀性,你必須例如瀏覽整個班級,看看是否有@Transient圍在那裏。
33

以下是您必須使用屬性訪問器的情況。想象一下,你有很多善良的實現一個通用的抽象類繼承分爲8個具體的子類:

public abstract class Foo<T extends Bar> { 

    T oneThing; 
    T anotherThing; 

    // getters and setters ommited for brevity 

    // Lots and lots of implementation regarding oneThing and anotherThing here 
} 

現在到底如何,你應該註釋這個類?答案是你根本無法用字段或屬性訪問對它進行註釋,因爲在這一點上你不能指定目標實體。你必須註解具體的實現。但是由於持久化屬性是在這個超類中聲明的,所以你必須在子類中使用屬​​性訪問。

字段訪問不是具有抽象通用超類的應用程序中的選項。

2

Are we there yet

這是一個古老的演示,但羅德認爲,在屬性訪問註釋鼓勵貧血的域模型,不應該是「默認」的方式來註釋字段訪問。

208

兩者都有參數,但其中大多數都源自某些用戶需求「如果需要添加邏輯」或「xxxx中斷封裝」。然而,沒有人真正評論過這個理論,並給出了一個合理的論據。

什麼是Hibernate/JPA實際上在保持對象的時候正在做什麼 - 嗯,它是持久化對象的狀態。這意味着以易於複製的方式存儲它。

什麼是封裝?封裝意味着使用應用程序/客戶端可安全訪問數據的接口封裝數據(或狀態) - 保持數據的一致性和有效性。

想想這就像MS Word一樣。 MS Word在內存中維護文檔的模型 - 文檔STATE。它提供了一個用戶可以用來修改文檔的界面 - 一組按鈕,工具,鍵盤命令等。但是,當您選擇保留(保存)該文檔時,它將保存內部狀態,而不是按鍵組和鼠標點擊用於生成它。

保存對象的內部狀態不會破壞封裝 - 否則你不會真正理解封裝是什麼意思,以及它爲什麼存在。這就像對象序列化一樣。

因此,在大多數情況下,堅持FIELDS而不是ACCESSORS是合適的。這意味着可以從數據庫準確地重新創建一個對象,與其存儲方式完全一致。它不應該需要任何驗證,因爲這是在創建時在原始數據上完成的,並且在它存儲在數據庫中之前(除非上帝保佑你將無效數據存儲在數據庫中!!!!)。同樣,不應該計算值,因爲它們在對象存儲之前已經計算出來了。該對象應該看起來就像它在保存之前所做的那樣。事實上,通過增加額外的東西到getters/setters中,你實際上會增加這個風險,你會重新創建一些不是原始副本的東西。

當然,添加此功能是有原因的。持久訪問者可能有一些有效的用例,但是,它們通常很少見。一個例子可能是你想避免保留一個計算的值,儘管你可能想問一個問題:爲什麼你不能在值的getter中按需求計算它,或者懶惰地在getter中初始化它。就我個人而言,我想不出任何好的用例,這裏的答案都沒有真正給出「軟件工程」的答案。

0

爲了讓你的類清潔劑,把標註在該領域再使用@Access(AccessType.PROPERTY)

2

我贊成場存取。代碼更清潔。所有的註釋可以放在一個類的一個 部分,代碼更容易閱讀。

我發現了屬性訪問器的另一個問題:如果您的類中沒有註明與持久屬性關聯的XYZ方法,hibernate會生成sql以嘗試獲取這些屬性,從而導致一些非常混亂的錯誤消息。浪費了兩個小時。我沒有寫這個代碼;過去我一直使用字段訪問器,從來沒有遇到過這個問題。在此應用程序中使用

Hibernate的版本:

<!-- hibernate --> 
<hibernate-core.version>3.3.2.GA</hibernate-core.version> 
<hibernate-annotations.version>3.4.0.GA</hibernate-annotations.version> 
<hibernate-commons-annotations.version>3.1.0.GA</hibernate-commons-annotations.version> 
<hibernate-entitymanager.version>3.4.0.GA</hibernate-entitymanager.version> 
1

我們創建了實體bean和使用吸氣註解。我們碰到的問題是:有些實體對於某些屬性何時可以更新有複雜的規則。解決方法是在每個設置器中都有一些業務邏輯,用於確定實際值是否發生變化,如果是,是否允許更改。當然,Hibernate總是可以設置屬性,所以我們結束了兩組設置器。很難看。

閱讀以前的帖子,我也看到從實體內引用屬性可能會導致集合不加載的問題。底線,我會傾向於註釋未來的領域。

12

如果你想在setter中做更多的事情而不僅僅是設置值(例如加密或計算),我強烈推薦field access和get annotations on the getters(property access)。

屬性訪問的問題在於setters也在對象加載時調用。在我們想要引入加密之前,這對我來說工作了好幾個月。在我們的用例中,我們想要在setter中加密一個字段並在getter中解密它。 屬性訪問現在的問題是,當Hibernate加載對象時,它也調用setter填充字段,因此再次對加密值進行加密。 這篇文章還提到: Java Hibernate: Different property set function behavior depending on who is calling it

這使我頭痛,直到我想起了字段訪問和屬性訪問之間的區別。現在我已將所有註釋從屬性訪問移至字段訪問,現在它工作正常。

2

另一個有利於字段訪問的觀點是,否則你不得不爲集合公開setters以及對於我來說,將持久集合實例更改爲一個不受Hibernate管理的對象是一個壞主意肯定會破壞你的數據一致性。

所以我更喜歡將集合作爲受保護的字段初始化爲默認構造函數中的空實現並僅公開他們的getter。然後,只有像clear(),remove(),removeAll()等託管操作是可能的,永遠不會讓Hibernate不知道更改。

2

我更喜歡字段,但我遇到了一種似乎迫使我將註釋放在getter上的情況。

對於Hibernate JPA實現,@Embedded似乎不適用於字段。所以這必須繼續吸氣。一旦你把它放在吸氣劑上,那麼各種註釋也必須放在吸氣劑上。 (我認爲Hibernate不希望混合字段和getter在這裏。)一旦你將@Column放在同一個類中的getter上,那麼整個過程都可能有意義。

4

我更喜歡使用的原因如下字段訪問:

  1. 屬性來實現equals /的hashCode和referencing fields directly時(如通過他們的getter反對)訪問可能導致非常討厭的錯誤。這是因爲代理只在訪問getters時被初始化,而直接字段訪問只會返回null。

  2. 屬性訪問需要你註釋所有utility methods(例如的addChild/removeChild之)作爲@Transient

  3. 通過字段訪問,我們可以根本不暴露getter來隱藏@Version字段。吸氣劑也可能導致添加吸氣劑,並且永遠不應手動設置該字段(這可能會導致非常討厭的問題)。所有版本增量應通過OPTIMISTIC_FORCE_INCREMENTPESSIMISTIC_FORCE_INCREMENT顯式鎖定觸發。

1

默認情況下,JPA提供商訪問實體字段的值,並使用映射實體的JavaBean屬性訪問器(獲取)和突變(setter方法)方法到數據庫列 這些領域。因此,實體中私有字段的名稱和類型與JPA無關。相反,JPA只查找 JavaBean屬性訪問器的名稱和返回類型。您可以使用@javax.persistence.Access註釋來更改此註釋,這使您可以明確指定JPA提供程序應使用的訪問方法 。

@Entity 
@Access(AccessType.FIELD) 
public class SomeEntity implements Serializable 
{ 
... 
} 

AccessType枚舉的可用選項是PROPERTY(默認值)和FIELD。使用 PROPERTY,提供者使用JavaBean屬性方法獲取並設置字段值。 FIELD使得 提供者使用實例字段獲取並設置字段值。作爲最佳實踐,您應該將 保留爲默認值,並使用JavaBean屬性,除非您有一個令人信服的理由否則。

您 可以將這些屬性註釋放在私有字段或公共存取方法中。如果 使用AccessType.PROPERTY(默認值)並註釋專用字段而不是JavaBean 訪問器,則字段名稱必須與JavaBean屬性名稱匹配。但是,如果註釋了JavaBean訪問器,名稱不必 必須匹配。同樣,如果使用AccessType.FIELD和 註釋JavaBean訪問器而不是字段,則字段名稱還必須與JavaBean 屬性名稱匹配。在這種情況下,如果您註釋字段,則不必匹配。最好是 應該一致並標註出AccessType.PROPERTY的JavaBean訪問器和 AccessType.FIELD的字段。

重要的是,您不應該將JPA屬性註釋和JPA字段註釋 混合在同一個實體中。這樣做會導致不確定的行爲,並且很可能導致錯誤。