java - सही जेपीए इकाई बनाएं




hibernate jpa (3)

मैं कुछ समय के लिए जेपीए (कार्यान्वयन हाइबरनेट) के साथ काम कर रहा हूं और हर बार जब मुझे संस्थाएं बनाने की ज़रूरत होती है तो मुझे लगता है कि मैं खुद को एक्सेसटाइप, अपरिवर्तनीय गुण, बराबर / हैशकोड, ... के रूप में समस्याओं से जूझ रहा हूं।
तो मैंने प्रत्येक मुद्दे के लिए सामान्य सर्वोत्तम अभ्यास करने का प्रयास करने और व्यक्तिगत उपयोग के लिए इसे लिखने का फैसला किया।
हालांकि, किसी के लिए इस पर टिप्पणी करने या मुझे यह बताने के लिए कि मैं कहां गलत हूं, मुझे कोई फर्क नहीं पड़ता।

इकाई वर्ग

  • Serializable लागू करें

    कारण: विनिर्देश कहता है कि आपको करना है, लेकिन कुछ जेपीए प्रदाता इसे लागू नहीं करते हैं। जेपीए प्रदाता के रूप में हाइबरनेट इसे लागू नहीं करता है, लेकिन यदि क्लासिकस्टेबल लागू नहीं किया गया है, तो यह क्लासकास्टएक्सप्शन के साथ अपने पेट में कहीं गहरा असफल हो सकता है।

कंस्ट्रक्टर्स

  • इकाई के सभी आवश्यक क्षेत्रों के साथ एक कन्स्ट्रक्टर बनाएँ

    कारण: एक कन्स्ट्रक्टर को हमेशा एक सनी स्थिति में बनाए गए उदाहरण को छोड़ देना चाहिए।

  • इस कन्स्ट्रक्टर के अलावा: एक पैकेज निजी डिफ़ॉल्ट कन्स्ट्रक्टर है

    कारण: हाइबरनेट को इकाई को प्रारंभ करने के लिए डिफ़ॉल्ट कन्स्ट्रक्टर की आवश्यकता होती है; निजी अनुमति है लेकिन प्लेटाइम प्रॉक्सी पीढ़ी और बाइटकोड उपकरण के बिना कुशल डेटा पुनर्प्राप्ति के लिए निजी (या सार्वजनिक) दृश्यता पैकेज आवश्यक है।

फील्ड्स / गुण

  • आवश्यकता होने पर सामान्य रूप से क्षेत्रीय पहुंच और संपत्ति पहुंच का उपयोग करें

    कारण: यह शायद सबसे बहस योग्य मुद्दा है क्योंकि एक या दूसरे (संपत्ति पहुंच बनाम फ़ील्ड एक्सेस) के लिए कोई स्पष्ट और दृढ़ तर्क नहीं है; हालांकि, क्षेत्रीय पहुंच स्पष्ट कोड की वजह से सामान्य पसंदीदा प्रतीत होती है, बेहतर encapsulation और अपरिवर्तनीय क्षेत्रों के लिए सेटर्स बनाने की कोई ज़रूरत नहीं है

  • अपरिवर्तनीय फ़ील्ड के लिए ओमिट सेटर्स (एक्सेस प्रकार फ़ील्ड के लिए आवश्यक नहीं)

  • संपत्ति निजी हो सकती है
    कारण: मैंने एक बार सुना है कि संरक्षित (हाइबरनेट) प्रदर्शन के लिए बेहतर है लेकिन वेब पर मुझे जो भी मिल सकता है वह है: हाइबरनेट सार्वजनिक, निजी और संरक्षित एक्सेसर विधियों के साथ-साथ सार्वजनिक, निजी और संरक्षित फ़ील्ड तक सीधे पहुंच सकता है। पसंद आपके ऊपर है और आप इसे अपने एप्लिकेशन डिज़ाइन के अनुरूप मिलान कर सकते हैं।

बराबर है / hashCode

  • उत्पन्न आईडी को कभी भी उपयोग न करें अगर यह आईडी केवल इकाई को बनाए रखने पर सेट हो
  • वरीयता से: अद्वितीय व्यापार कुंजी बनाने के लिए अपरिवर्तनीय मूल्यों का उपयोग करें और समानता का परीक्षण करने के लिए इसका उपयोग करें
  • यदि एक अद्वितीय बिजनेस कुंजी उपलब्ध नहीं है तो एक गैर-क्षणिक यूयूआईडी का उपयोग करें जो इकाई प्रारंभ होने पर बनाई जाती है; अधिक जानकारी के लिए यह महान लेख देखें।
  • कभी संबंधित संस्थाओं (ManyToOne) का संदर्भ लें; यदि यह इकाई (एक मूल इकाई की तरह) को बिजनेस कुंजी का हिस्सा बनने की आवश्यकता है तो आईडी की तुलना करें। प्रॉक्सी पर getId () को कॉल करना इकाई की लोडिंग को ट्रिगर नहीं करेगा, जब तक आप संपत्ति एक्सेस प्रकार का उपयोग कर रहे हों।

उदाहरण इकाई

@Entity
@Table(name = "ROOM")
public class Room implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    @Column(name = "room_id")
    private Integer id;

    @Column(name = "number") 
    private String number; //immutable

    @Column(name = "capacity")
    private Integer capacity;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "building_id")
    private Building building; //immutable

    Room() {
        // default constructor
    }

    public Room(Building building, String number) {
        // constructor with required field
        notNull(building, "Method called with null parameter (application)");
        notNull(number, "Method called with null parameter (name)");

        this.building = building;
        this.number = number;
    }

    @Override
    public boolean equals(final Object otherObj) {
        if ((otherObj == null) || !(otherObj instanceof Room)) {
            return false;
        }
        // a room can be uniquely identified by it's number and the building it belongs to; normally I would use a UUID in any case but this is just to illustrate the usage of getId()
        final Room other = (Room) otherObj;
        return new EqualsBuilder().append(getNumber(), other.getNumber())
                .append(getBuilding().getId(), other.getBuilding().getId())
                .isEquals();
        //this assumes that Building.id is annotated with @Access(value = AccessType.PROPERTY) 
    }

    public Building getBuilding() {
        return building;
    }


    public Integer getId() {
        return id;
    }

    public String getNumber() {
        return number;
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(getNumber()).append(getBuilding().getId()).toHashCode();
    }

    public void setCapacity(Integer capacity) {
        this.capacity = capacity;
    }

    //no setters for number, building nor id

}

इस सूची में जोड़ने के लिए अन्य सुझाव स्वागत से अधिक हैं ...

अद्यतन करें

इस आलेख को पढ़ने के बाद से मैंने eq / hC को लागू करने का अपना तरीका अनुकूलित किया है:

  • यदि एक अपरिवर्तनीय सरल व्यवसाय कुंजी उपलब्ध है: इसका उपयोग करें
  • अन्य सभी मामलों में: यूयूआईडी का उपयोग करें

Stijns अर्ध-व्यापक सूची में मेरी प्रशंसा व्यक्त करने के बाद, 2 सुधार हैं:

  1. फील्ड या प्रॉपर्टी एक्सेस (प्रदर्शन विचारों से दूर) के संदर्भ में दोनों को गेटर्स और सेटर्स के माध्यम से वैध रूप से एक्सेस किया जाता है, इस प्रकार, मेरा मॉडल तर्क उन्हें उसी तरह से सेट / प्राप्त कर सकता है। अंतर तब चलने लगता है जब दृढ़ता रनटाइम प्रदाता (हाइबरनेट, ग्रहण लिंक या अन्य) को तालिका ए में कुछ रिकॉर्ड जारी रखने / सेट करने की आवश्यकता होती है, जिसमें तालिका बी में कुछ कॉलम का जिक्र करते हुए एक विदेशी कुंजी होती है। संपत्ति पहुंच प्रकार के मामले में, दृढ़ता रनटाइम सिस्टम टेबल बी कॉलम में सेल को एक नया मान निर्दिष्ट करने के लिए मेरे कोडित सेटर विधि का उपयोग करता है। फ़ील्ड एक्सेस प्रकार के मामले में, दृढ़ता रनटाइम सिस्टम तालिका बी कॉलम में सेल को सीधे सेट करता है। यह अंतर एक गैर-दिशात्मक संबंध के संदर्भ में महत्व का नहीं है, फिर भी यह एक द्वि-दिशात्मक संबंध के लिए अपने स्वयं के कोडित सेटर विधि (संपत्ति पहुंच प्रकार) का उपयोग करना आवश्यक है बशर्ते सेटटर विधि अच्छी तरह से स्थिरता के लिए जिम्मेदार हो । द्वि-दिशात्मक संबंधों के लिए संगठनात्मकता एक महत्वपूर्ण मुद्दा है जो इस link को एक अच्छी तरह से डिज़ाइन किए गए सेटटर के लिए एक साधारण उदाहरण के लिए संदर्भित करता है।

  2. समान / हैशकोड के संदर्भ में: द्वि-दिशात्मक संबंध में भाग लेने वाली संस्थाओं के लिए ग्रहण ऑटो-जेनरेटेड बराबर / हैशकोड विधियों का उपयोग करना असंभव है, अन्यथा उनके पास एक परिपत्र संदर्भ होगा जिसके परिणामस्वरूप स्टैक ओवरफ्लो अपवाद हो। एक बार जब आप एक द्विपक्षीय संबंध (OneToOne कहें) और ऑटो-जेनरेट इक्विल्स () या हैशकोड () या यहां तक ​​कि toString () को भी कोशिश करते हैं तो आप इस अपवाद में पकड़े जाएंगे।


इकाई इंटरफेस

public interface Entity<I> extends Serializable {

/**
 * @return entity identity
 */
I getId();

/**
 * @return HashCode of entity identity
 */
int identityHashCode();

/**
 * @param other
 *            Other entity
 * @return true if identities of entities are equal
 */
boolean identityEquals(Entity<?> other);
}

सभी संस्थाओं के लिए बुनियादी कार्यान्वयन, बराबर / हैशकोड कार्यान्वयन को सरल बनाता है:

public abstract class AbstractEntity<I> implements Entity<I> {

@Override
public final boolean identityEquals(Entity<?> other) {
    if (getId() == null) {
        return false;
    }
    return getId().equals(other.getId());
}

@Override
public final int identityHashCode() {
    return new HashCodeBuilder().append(this.getId()).toHashCode();
}

@Override
public final int hashCode() {
    return identityHashCode();
}

@Override
public final boolean equals(final Object o) {
    if (this == o) {
        return true;
    }
    if ((o == null) || (getClass() != o.getClass())) {
        return false;
    }

    return identityEquals((Entity<?>) o);
}

@Override
public String toString() {
    return getClass().getSimpleName() + ": " + identity();
    // OR 
    // return ReflectionToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
}
}

कक्ष इकाई प्रत्यारोपण:

@Entity
@Table(name = "ROOM")
public class Room extends AbstractEntity<Integer> {

private static final long serialVersionUID = 1L;

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

@Column(name = "number") 
private String number; //immutable

@Column(name = "capacity")
private Integer capacity;

@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "building_id")
private Building building; //immutable

Room() {
    // default constructor
}

public Room(Building building, String number) {
    // constructor with required field
    notNull(building, "Method called with null parameter (application)");
    notNull(number, "Method called with null parameter (name)");

    this.building = building;
    this.number = number;
}

public Integer getId(){
    return id;
}

public Building getBuilding() {
    return building;
}

public String getNumber() {
    return number;
}


public void setCapacity(Integer capacity) {
    this.capacity = capacity;
}

//no setters for number, building nor id
}

मुझे जेपीए इकाइयों के हर मामले में व्यावसायिक क्षेत्रों के आधार पर इकाइयों की समानता की तुलना करने का एक बिंदु नहीं दिखता है। यह एक मामला अधिक हो सकता है यदि इन जेपीए इकाइयों को डोमेन-संचालित इकाइयों के बजाय डोमेन-संचालित वैल्यू ऑब्जेक्ट्स के रूप में माना जाता है (जो इन कोड उदाहरण हैं)।


जेपीए 2.0 विशिष्टता बताती है कि:

  • इकाई वर्ग में नो-एर्ग कन्स्ट्रक्टर होना चाहिए। इसमें अन्य रचनाकार भी हो सकते हैं। नो-Arg कन्स्ट्रक्टर सार्वजनिक या संरक्षित होना चाहिए।
  • इकाई वर्ग शीर्ष स्तर की कक्षा होना चाहिए। एक enum या इंटरफेस एक इकाई के रूप में नामित नहीं किया जाना चाहिए।
  • इकाई वर्ग अंतिम नहीं होना चाहिए। इकाई वर्ग का कोई तरीका या लगातार उदाहरण चर अंतिम नहीं हो सकता है।
  • यदि एक इकाई उदाहरण को एक अलग वस्तु के रूप में मान द्वारा पारित किया जाना है (उदाहरण के लिए, दूरस्थ इंटरफ़ेस के माध्यम से), इकाई वर्ग को Serializable इंटरफ़ेस को कार्यान्वित करना होगा।
  • अमूर्त और ठोस कक्षाएं दोनों संस्थाएं हो सकती हैं। संस्थाएं गैर-इकाई वर्गों के साथ-साथ इकाई वर्गों का विस्तार कर सकती हैं, और गैर-इकाई वर्ग इकाई वर्गों का विस्तार कर सकते हैं।

विनिर्देश में इकाइयों के लिए बराबर और हैशकोड विधियों के कार्यान्वयन के बारे में कोई आवश्यकता नहीं है, केवल प्राथमिक कुंजी वर्गों और मानचित्र कुंजी के लिए जहां तक ​​मुझे पता है।





equals