JPA: एक ही इकाई प्रकार के एक-से-कई संबंध कैसे हैं


101

वहाँ एक एंटिटी क्लास "ए" है। कक्षा ए में एक ही प्रकार के बच्चे हो सकते हैं "ए"। यदि बच्चा है तो "ए" को उसके माता-पिता को पकड़ना चाहिए।

क्या यह संभव है? यदि हां, तो मुझे इकाई वर्ग में संबंधों को कैसे मैप करना चाहिए? ["A" में एक आईडी कॉलम है।]

जवाबों:


171

हां, यह संभव है। यह मानक द्विदिश @ManyToOne/ @OneToManyसंबंध का एक विशेष मामला है । यह विशेष है क्योंकि संबंध के प्रत्येक छोर पर इकाई समान है। सामान्य मामला JPA 2.0 कल्पना की धारा 2.10.2 में विस्तृत है ।

यहां एक उदाहरण दिया गया है। सबसे पहले, इकाई वर्ग A:

@Entity
public class A implements Serializable {

    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;
    @ManyToOne
    private A parent;
    @OneToMany(mappedBy="parent")
    private Collection<A> children;

    // Getters, Setters, serialVersionUID, etc...
}

यहाँ एक कठिन main()तरीका है जो तीन ऐसी संस्थाओं को बनाए रखता है:

public static void main(String[] args) {

    EntityManager em = ... // from EntityManagerFactory, injection, etc.

    em.getTransaction().begin();

    A parent   = new A();
    A son      = new A();
    A daughter = new A();

    son.setParent(parent);
    daughter.setParent(parent);
    parent.setChildren(Arrays.asList(son, daughter));

    em.persist(parent);
    em.persist(son);
    em.persist(daughter);

    em.getTransaction().commit();
}

इस मामले में, लेनदेन करने से पहले सभी तीन इकाई उदाहरणों को बरकरार रखा जाना चाहिए। यदि मैं माता-पिता-बच्चे के रिश्तों के ग्राफ में किसी एक संस्था को बनाए रखने में विफल रहता हूं, तो एक अपवाद को फेंक दिया जाता है commit()। एक्लिप्सिंक पर, यह RollbackExceptionविसंगति का विवरण है।

यह व्यवहार 's और एनोटेशन cascadeपर विशेषता के माध्यम से विन्यास योग्य है । उदाहरण के लिए, यदि मैं उन दोनों एनोटेशनों पर सेट करता हूं, तो मैं सुरक्षित रूप से किसी एक संस्था को बनाए रख सकता हूं और दूसरों को अनदेखा कर सकता हूं। कहते हैं कि मैं अपने लेन-देन में कायम रहा । जेपीए कार्यान्वयन traverses के संपत्ति है क्योंकि यह के साथ चिह्नित है । जेपीए कार्यान्वयन वहाँ और वहाँ पाता है । यह तब भी मेरी ओर से दोनों बच्चों को जारी रखता है, भले ही मैंने स्पष्ट रूप से इसका अनुरोध न किया हो।A@OneToMany@ManyToOnecascade=CascadeType.ALLparentparentchildrenCascadeType.ALLsondaughter

एक और नोट। यह हमेशा प्रोग्रामर की जिम्मेदारी होती है कि वह एक द्विदिश संबंध के दोनों पक्षों को अपडेट करे। दूसरे शब्दों में, जब भी मैं किसी माता-पिता के लिए एक बच्चा जोड़ता हूं, मुझे उसके अनुसार बच्चे की मूल संपत्ति को अपडेट करना चाहिए। एक द्विदिश संबंध के केवल एक पक्ष को अपडेट करना जेपीए के तहत एक त्रुटि है। रिश्ते के दोनों किनारों को हमेशा अपडेट करें। यह JPA 2.0 कल्पना के पृष्ठ 42 पर असंदिग्ध रूप से लिखा गया है:

ध्यान दें कि यह एप्लिकेशन है जो रनटाइम रिश्तों की निरंतरता बनाए रखने के लिए जिम्मेदारी वहन करता है - उदाहरण के लिए, यह सुनिश्चित करने के लिए कि एक द्विदिश संबंध के "एक" और "कई" पक्ष एक दूसरे के साथ संगत हैं जब एप्लिकेशन रनटाइम पर संबंध को अपडेट करता है। ।


विस्तृत विवरण के लिए आपका बहुत-बहुत धन्यवाद! उदाहरण बिंदु पर है और पहले भाग में काम किया है।
सांझयाव

@sunnyj खुशी मदद करने के लिए। आपकी परियोजना के लिए शुभकामनाएँ।
Dan LaRocque

यह श्रेणी इकाई बनाते समय पहले इस मुद्दे से मुलाकात की जिसमें उप श्रेणियां हैं। ये उपयोगी है!
Truong Ha

@DanLaRocque शायद मुझे गलतफहमी हो रही है (या एक इकाई मानचित्रण त्रुटि है), लेकिन मैं अप्रत्याशित व्यवहार देख रहा हूं। मेरे उपयोगकर्ता और पते के बीच एक-से-कई संबंध हैं। जब कोई मौजूदा उपयोगकर्ता एक पता जोड़ता है, तो मैंने उपयोगकर्ता और पते दोनों को अपडेट करने के लिए आपके सुझाव का पालन किया (और दोनों पर 'सेव' को कॉल करें)। लेकिन इसके परिणामस्वरूप एक डुप्लिकेट पंक्ति को मेरी पता तालिका में डाला गया। क्या इसलिए कि मैंने उपयोगकर्ता के पते के क्षेत्र पर अपने CascadeType को गलत तरीके से बनाया है?
एलेक्स

@DanLaRocque इस रिश्ते को एकतरफा मानना ​​संभव है ??
अली अरदा ओरहान

8

मेरे लिए यह ट्रिक कई-कई संबंधों का उपयोग करने के लिए थी। मान लीजिए कि आपकी इकाई A एक ऐसा डिवीजन है जिसमें सब-डिवीजन हो सकते हैं। तब (अप्रासंगिक विवरणों को छोड़ते हुए):

@Entity
@Table(name = "DIVISION")
@EntityListeners( { HierarchyListener.class })
public class Division implements IHierarchyElement {

  private Long id;

  @Id
  @Column(name = "DIV_ID")
  public Long getId() {
        return id;
  }
  ...
  private Division parent;
  private List<Division> subDivisions = new ArrayList<Division>();
  ...
  @ManyToOne
  @JoinColumn(name = "DIV_PARENT_ID")
  public Division getParent() {
        return parent;
  }

  @ManyToMany
  @JoinTable(name = "DIVISION", joinColumns = { @JoinColumn(name = "DIV_PARENT_ID") }, inverseJoinColumns = { @JoinColumn(name = "DIV_ID") })
  public List<Division> getSubDivisions() {
        return subDivisions;
  }
...
}

चूँकि मेरे पास पदानुक्रमित संरचना और JPA (रिलेशनल मॉडल के आधार पर) के आसपास कुछ व्यापक व्यावसायिक तर्क थे, इसलिए इसका समर्थन करने के लिए बहुत कमजोर हूं मैंने इंटरफ़ेस IHierarchyElementऔर इकाई श्रोता पेश किया HierarchyListener:

public interface IHierarchyElement {

    public String getNodeId();

    public IHierarchyElement getParent();

    public Short getLevel();

    public void setLevel(Short level);

    public IHierarchyElement getTop();

    public void setTop(IHierarchyElement top);

    public String getTreePath();

    public void setTreePath(String theTreePath);
}


public class HierarchyListener {

    @PrePersist
    @PreUpdate
    public void setHierarchyAttributes(IHierarchyElement entity) {
        final IHierarchyElement parent = entity.getParent();

        // set level
        if (parent == null) {
            entity.setLevel((short) 0);
        } else {
            if (parent.getLevel() == null) {
                throw new PersistenceException("Parent entity must have level defined");
            }
            if (parent.getLevel() == Short.MAX_VALUE) {
                throw new PersistenceException("Maximum number of hierarchy levels reached - please restrict use of parent/level relationship for "
                        + entity.getClass());
            }
            entity.setLevel(Short.valueOf((short) (parent.getLevel().intValue() + 1)));
        }

        // set top
        if (parent == null) {
            entity.setTop(entity);
        } else {
            if (parent.getTop() == null) {
                throw new PersistenceException("Parent entity must have top defined");
            }
            entity.setTop(parent.getTop());
        }

        // set tree path
        try {
            if (parent != null) {
                String parentTreePath = StringUtils.isNotBlank(parent.getTreePath()) ? parent.getTreePath() : "";
                entity.setTreePath(parentTreePath + parent.getNodeId() + ".");
            } else {
                entity.setTreePath(null);
            }
        } catch (UnsupportedOperationException uoe) {
            LOGGER.warn(uoe);
        }
    }

}

1
स्वयं-संदर्भित विशेषताओं के साथ @ManyToMany (...) के स्थान पर सरल @OneToMany (mappedBy = "DIV_PARENT_ID") का उपयोग क्यों नहीं करें? तालिका और स्तंभ नामों को फिर से लेना जैसे DRY का उल्लंघन करता है। हो सकता है कि इसका कोई कारण हो, लेकिन मैं इसे नहीं देखता। इसके अलावा, EntityListener का उदाहरण साफ-सुथरा है, लेकिन गैर-पोर्टेबल है, यह मानना Topएक रिश्ता है। JPA 2.0 कल्पना, एंटिटी श्रोताओं और कॉलबैक विधियों के पेज 93: "सामान्य तौर पर, एक पोर्टेबल एप्लिकेशन के जीवनचक्र विधि को एंटिटी मैनजर या क्वेरी संचालन, अन्य इकाई उदाहरणों का उपयोग या रिश्तों को संशोधित नहीं करना चाहिए"। सही? मुझे पता है कि क्या मैं बंद हूँ।
डेन लॉरोक

मेरा समाधान JPA 1.0 का उपयोग करके 3 साल पुराना है। मैंने इसे उत्पादन कोड से अपरिवर्तित किया। मुझे यकीन है कि मैं कुछ कॉलम नामों को निकाल सकता हूं लेकिन यह बात नहीं थी। आपका उत्तर बिल्कुल सटीक और सरल है, यह निश्चित नहीं है कि मैंने तब कई-से-कई वापस क्यों इस्तेमाल किया - लेकिन यह काम करता है और मुझे विश्वास है कि एक कारण के लिए और अधिक जटिल समाधान था। हालाँकि, मुझे इस पर फिर से विचार करना होगा।
टॉपचेफ

हां, शीर्ष एक आत्म-संदर्भ है, इसलिए एक संबंध है। सख्ती से बोलना, मैं इसे संशोधित नहीं करता हूं - बस आरंभ करें। इसके अलावा, यह यूनिडायरेक्शनल है इसलिए चारों ओर कोई निर्भरता नहीं है, यह स्वयं के अलावा अन्य संस्थाओं का संदर्भ नहीं देता है। आपके उद्धरण के अनुसार कल्पना में "सामान्य रूप" है जिसका अर्थ है कि यह सख्त परिभाषा नहीं है। मेरा मानना ​​है कि इस मामले में यदि कोई है तो बहुत कम पोर्टेबिलिटी जोखिम है।
topchef
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.