2011-04-27 13 views
12

मैं जेपीए/हाइबरनेट का उपयोग कर निम्न तालिकाओं स्थापित करने के लिए कोशिश कर रहा हूँ प्राथमिक:OneToOne साझा कुंजी

User: 

userid - PK 
name 

Validation: 

userid - PK, FK(user) 
code 

कई उपयोगकर्ताओं हो सकता है और प्रत्येक उपयोगकर्ता के अधिकतम एक सत्यापन कोड या बिल्कुल भी नहीं हो सकता है।

यहाँ मेरी वर्गों है:

public class User 
{ 
    @Id 
    @Column(name = "userid") 
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    protected Long userId; 

    @Column(name = "name", length = 50, unique = true, nullable = false) 
    protected String name; 

    ... 
} 

public class Validation 
{ 
    @Id 
    @Column(name = "userid") 
    protected Long userId; 

    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid") 
    protected User user; 

    @Column(name = "code", length = 10, unique = true, nullable = false) 
    protected String code; 

    ... 

    public void setUser(User user) 
    { 
     this.user = user; 
     this.userId = user.getUserId(); 
    } 

    ... 
} 

मैं एक उपयोगकर्ता बना सकते हैं और उसके बाद निम्न कोड का उपयोग कर एक सत्यापन कोड को जोड़ने का प्रयास:

public void addValidationCode(Long userId) 
{ 
    EntityManager em = createEntityManager(); 
    EntityTransaction tx = em.getTransaction(); 

    try 
    { 
     tx.begin(); 

     // Fetch the user 
     User user = retrieveUserByID(userId); 

     Validation validation = new Validation(); 
     validation.setUser(user); 
     em.persist(validation); 
     tx.commit(); 
    } 
    ... 
} 

जब मैं इसे चलाने के लिए मैं एक ऑर्ग पाने का प्रयास करें। hibernate.PersistentObjectException: पृथक इकाई को जारी रखने के लिए पारित किया गया: उपयोगकर्ता

मैंने अपने सत्यापन वर्ग में निम्न कोड का उपयोग करने का भी प्रयास किया है:

public void setUserId(Long userId) 
{ 
    this.userId = userId; 
} 

और जब मैं एक सत्यापन कोड बनाने मैं बस कार्य करें:

Validation validation = new Validation(); 
validation.setUserId(userId); 
em.persist(validation); 
tx.commit(); 

लेकिन तब से उपयोगकर्ता रिक्त है मैं org.hibernate.PropertyValueException मिलती है: नहीं-अशक्त संपत्ति एक अशक्त या क्षणिक मानों का संदर्भ: उपयोगकर्ता .code

इस समस्या को हल करने के तरीके के बारे में किसी भी मदद की सराहना करेंगे!

उत्तर

9

आप हाइबरनेट का उपयोग करते हैं आप भी उपयोग

public class Validation { 

    private Long validationId; 
    private User user; 

    @Id 
    @GeneratedValue(generator="SharedPrimaryKeyGenerator") 
    @GenericGenerator(name="SharedPrimaryKeyGenerator",strategy="foreign",parameters = @Parameter(name="property", value="user")) 
    @Column(name = "VALIDATION_ID", unique = true, nullable = false) 
    public Long getValidationId(){ 
     return validationId; 
    } 

    @OneToOne 
    @PrimaryKeyJoinColumn 
    public User getUser() { 
     return user; 
    } 

} 

हाइबरनेट सुनिश्चित करें कि मान्यता की आईडी उपयोगकर्ता इकाई सेट की ID के समान हो जाएगा कर देगा कर सकते हैं।

3

आपको userId और user दोनों सेट करने की आवश्यकता है।

यदि आप केवल user सेट करते हैं, तो Validation 0 के लिए 0 है और इसे अलग समझा जाता है। यदि आप केवल userId सेट करते हैं, तो आपको user संपत्ति को शून्य बनाने की आवश्यकता है, जो यहां समझ में नहीं आता है।

सुरक्षा के लिए, आप शायद उन दोनों को एक विधि कॉल में सेट कर सकते हैं:

@Transient 
public void setUserAndId(User user){ 
    this.userId = user.getId(); 
    this.user = user; 
} 

मैं विधि @Transient चिह्नित ताकि हाइबरनेट यह ध्यान नहीं देगा। साथ ही, आप अभी भी setUser और setUserId किसी भी "साइड इफेक्ट्स" के साथ अपेक्षित काम कर सकते हैं।

4

क्या आप जेपीए या जेपीए 2.0 का उपयोग कर रहे हैं?

यदि प्रमाणीकरण पीके उपयोगकर्ता के लिए एक एफके है, तो आपको सत्यापन कक्षा में लंबे उपयोगकर्ता आईडी विशेषता की आवश्यकता नहीं है, बल्कि इसके बजाय @Id अकेले एनोटेशन करें। यह होगा:

Public class Validation 
{ 
    @Id 
    @OneToOne(cascade = CascadeType.ALL) 
    @PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid") 
    protected User user; 

    @Column(name = "code", length = 10, unique = true, nullable = false) 
    protected String code; 

    ... 

    public void setUser(User user) 
    { 
     this.user = user; 
     this.userId = user.getUserId(); 
    } 

    ... 
} 

इसके साथ प्रयास करें और हमें अपने परिणाम बताएं।

+0

+1: यह आदर्श होगा। – Jeremy

+0

जब आप इसे परिभाषित नहीं करते हैं तो 'setUser()' में 'userId' फ़ील्ड को कैसे सेट कर सकते हैं? – Lu55

12

मैं शुद्ध जेपीए 2.0 तरीके (एसओएफ पर कई मौजूदा धागे के लिए धन्यवाद) में "साझा प्राथमिक कुंजी के साथ दो तालिकाओं के बीच OneToOne" की इस समस्या को हल करने में सक्षम हूं।वास्तव में इसे संभालने के लिए जेपीए में दो तरीके हैं। मैंने जेपीए प्रदाता और MySQL को डेटाबेस के रूप में eclipselink का उपयोग किया है। एक बार फिर से हाइलाइट करने के लिए यहां कोई स्वामित्व वाली ग्रहण कक्षाएं उपयोग नहीं की गई हैं।

  1. पहला दृष्टिकोण माता-पिता इकाई के पहचानकर्ता फ़ील्ड पर ऑटो जनरेशन प्रकार रणनीति का उपयोग करना है।

    • पेरेंट इकाई OneToOne संबंध में बाल इकाई प्रकार के सदस्य (झरना प्रकार जारी रहती है और बाल इकाई की mappedBy = मूल इकाई प्रकार सदस्य)

      @Entity 
      @Table(name = "USER_LOGIN") 
      public class UserLogin implements Serializable { 
          @Id 
          @GeneratedValue(strategy = GenerationType.AUTO) 
          @Column(name="USER_ID") 
          private Integer userId; 
      
          @OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin") 
          private UserDetail userDetail; 
      // getters & setters 
      } 
      
    • बाल इकाई एक पहचानकर्ता क्षेत्र शामिल नहीं करना चाहिए शामिल होना चाहिए । इसमें आईडी, वनटोन और जॉइन कॉलम एनोटेशन के साथ अभिभावक इकाई प्रकार का सदस्य होना चाहिए। JoinColumn को डीबी तालिका का आईडी फ़ील्ड नाम निर्दिष्ट करना होगा।

      @Entity 
      @Table(name = "USER_DETAIL") 
      public class UserDetail implements Serializable { 
          @Id 
          @OneToOne 
          @JoinColumn(name="USER_ID") 
          private UserLogin userLogin; 
      // getters & setters 
      } 
      
    • दृष्टिकोण से ऊपर आंतरिक पहचानकर्ता क्षेत्र के लिए मान निर्दिष्ट के लिए एक डिफ़ॉल्ट डीबी तालिका SEQUENCE नामित उपयोग करता है। यदि पहले से मौजूद नहीं है, तो इस तालिका को नीचे के रूप में बनाया जाना चाहिए।

      DROP TABLE TEST.SEQUENCE ; 
      CREATE TABLE TEST.SEQUENCE (SEQ_NAME VARCHAR(50), SEQ_COUNT DECIMAL(15)); 
      INSERT INTO TEST.SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0); 
      
  2. दूसरा दृष्टिकोण पेरेंट इकाई के पहचानकर्ता मैदान पर अनुकूलित टेबल पीढ़ी प्रकार की रणनीति और TableGenerator एनोटेशन का प्रयोग है।

    • पहचानकर्ता फ़ील्ड में उपरोक्त परिवर्तन को छोड़कर बाकी सब कुछ अभिभावक इकाई में अपरिवर्तित बनी हुई है।

      @Entity 
      @Table(name = "USER_LOGIN") 
      public class UserLogin implements Serializable { 
          @Id 
          @TableGenerator(name="tablegenerator", table = "APP_SEQ_STORE", pkColumnName = "APP_SEQ_NAME", pkColumnValue = "USER_LOGIN.USER_ID", valueColumnName = "APP_SEQ_VALUE", initialValue = 1, allocationSize = 1) 
          @GeneratedValue(strategy = GenerationType.TABLE, generator = "tablegenerator") 
          @Column(name="USER_ID") 
          private Integer userId; 
      
          @OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin") 
          private UserDetail userDetail; 
      // getters & setters 
      } 
      
    • बाल संस्था में कोई बदलाव नहीं है। यह पहले दृष्टिकोण के समान ही बना हुआ है।

    • यह तालिका जेनरेटर दृष्टिकोण पहचानकर्ता फ़ील्ड को मान निर्दिष्ट करने के लिए आंतरिक रूप से डीबी तालिका APP_SEQ_STORE का उपयोग करता है। इस तालिका को नीचे के रूप में बनाया जाना चाहिए।

      DROP TABLE TEST.APP_SEQ_STORE; 
      CREATE TABLE TEST.APP_SEQ_STORE 
      (
          APP_SEQ_NAME VARCHAR(255) NOT NULL, 
          APP_SEQ_VALUE BIGINT NOT NULL, 
          PRIMARY KEY(APP_SEQ_NAME) 
      ); 
      INSERT INTO TEST.APP_SEQ_STORE VALUES ('USER_LOGIN.USER_ID', 0); 
      
+1

जब आप UserLogin को लोड करने का प्रयास करते हैं तो पहला दृष्टिकोण आपको अनंत लूप नहीं देता है? – squallsv

संबंधित मुद्दे