जब getOne और findOne के तरीकों का उपयोग करें स्प्रिंग डेटा JPA


154

मेरे पास एक उपयोग का मामला है जहां यह निम्नलिखित कहता है:

@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
    return this.userControlRepository.getOne(id);
}

निरीक्षण @Transactionalहै Propagation.REQUIRES_NEW और भंडार का उपयोग करता है getOne । जब मैं एप्लिकेशन चलाता हूं, तो मुझे निम्न त्रुटि संदेश प्राप्त होता है:

Exception in thread "main" org.hibernate.LazyInitializationException: 
could not initialize proxy - no Session
...

लेकिन अगर मैं बदल getOne(id)द्वारा findOne(id)सभी कार्यों ठीक।

BTW, बस उपयोग के मामले में getUserControlById विधि को कॉल करने से पहले, इसे पहले से ही InsertUserControl विधि कहा जाता है

@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
    return this.userControlRepository.save(userControl);
}

दोनों विधियाँ Propagation.REQUIRES_NEW हैं क्योंकि मैं एक साधारण ऑडिट नियंत्रण कर रहा हूँ ।

मैं getOneविधि का उपयोग करता हूं क्योंकि यह JpaRepository इंटरफ़ेस में परिभाषित किया गया है और मेरा रिपॉजिटरी इंटरफ़ेस वहाँ से फैला है, मैं निश्चित रूप से JPA के साथ काम कर रहा हूं।

JpaRepository इंटरफ़ेस से फैली CrudRepository । में findOne(id)विधि को परिभाषित किया गया है CrudRepository

मेरे प्रश्न हैं:

  1. getOne(id)विधि क्यों विफल ?
  2. मुझे getOne(id)विधि का उपयोग कब करना चाहिए ?

मैं अन्य रिपॉजिटरी के साथ काम कर रहा हूं और सभी getOne(id)विधि का उपयोग करते हैं और सभी ठीक काम करते हैं, केवल जब मैं Propagation.REQUIRES_NEW का उपयोग करता हूं तो यह विफल हो जाता है।

GetOne API के अनुसार :

दिए गए पहचानकर्ता के साथ इकाई का संदर्भ देता है।

FindOne API के अनुसार :

किसी संस्था को उसकी आईडी से वापस लेता है।

3) जब मुझे findOne(id)विधि का उपयोग करना चाहिए ?

4) किस विधि का उपयोग करने की सिफारिश की जाती है?

अग्रिम में धन्यवाद।


आपको विशेष रूप से डेटा बेस में किसी ऑब्जेक्ट के अस्तित्व का परीक्षण करने के लिए getOne () का उपयोग नहीं करना चाहिए, क्योंकि getOne के साथ आपको एक ऑब्जेक्ट मिलता है! = Null, जबकि findOne null डिलीवर करता है।
Uwe Allner

जवाबों:


137

टी एल; डॉ

T findOne(ID id)(पुराने एपीआई में नाम) / Optional<T> findById(ID id)(नए एपीआई में नाम) पर निर्भर करता है EntityManager.find()कि एक इकाई उत्सुक लोड हो रहा है

T getOne(ID id)EntityManager.getReference()उस पर निर्भर करता है एक इकाई आलसी लोडिंग करता है । इसलिए इकाई के प्रभावी लोडिंग को सुनिश्चित करने के लिए, इस पर एक विधि लागू करना आवश्यक है।

findOne()/findById()की तुलना में उपयोग करने के लिए वास्तव में अधिक स्पष्ट और सरल है getOne()
तो मामलों की बहुत सबसे में, एहसान findOne()/findById()से अधिक getOne()


एपीआई परिवर्तन

कम से कम, 2.0संस्करण, Spring-Data-Jpaसंशोधित findOne()
पहले, इसे इस रूप में परिभाषित किया गया था CrudRepository:

T findOne(ID primaryKey);

अब, आपको जो एकल findOne()विधि मिलेगी CrudRepository, वह QueryByExampleExecutorइंटरफ़ेस में परिभाषित की गई है:

<S extends T> Optional<S> findOne(Example<S> example);

यह अंत SimpleJpaRepositoryमें CrudRepositoryइंटरफ़ेस के डिफ़ॉल्ट कार्यान्वयन द्वारा कार्यान्वित किया जाता है।
यह विधि उदाहरण खोज द्वारा एक क्वेरी है और आप इसे प्रतिस्थापन के रूप में नहीं चाहते हैं।

वास्तव में, नए API में समान व्यवहार वाला तरीका अभी भी है लेकिन विधि नाम बदल गया है।
यह से नाम दिया गया था findOne()करने के लिए findById()में CrudRepositoryइंटरफ़ेस:

Optional<T> findById(ID id); 

अब यह रिटर्न ए Optional। जिसे रोकना इतना बुरा नहीं है NullPointerException

तो, वास्तविक विकल्प अब Optional<T> findById(ID id)और के बीच है T getOne(ID id)


दो अलग-अलग विधियाँ जो दो अलग-अलग JPA EntityManager पुनर्प्राप्ति विधियों पर निर्भर करती हैं

1) Optional<T> findById(ID id)जावदोक में कहा गया है कि:

किसी संस्था को उसकी आईडी से वापस लेता है।

जैसा कि हम कार्यान्वयन में देखते हैं, हम देख सकते हैं कि यह EntityManager.find()पुनर्प्राप्ति करने के लिए निर्भर करता है :

public Optional<T> findById(ID id) {

    Assert.notNull(id, ID_MUST_NOT_BE_NULL);

    Class<T> domainType = getDomainClass();

    if (metadata == null) {
        return Optional.ofNullable(em.find(domainType, id));
    }

    LockModeType type = metadata.getLockModeType();

    Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();

    return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}

और यहाँ em.find()एक EntityManagerविधि के रूप में घोषित किया गया है:

public <T> T find(Class<T> entityClass, Object primaryKey,
                  Map<String, Object> properties);

इसका जावाडॉक कहता है:

निर्दिष्ट गुणों का उपयोग करके प्राथमिक कुंजी द्वारा खोजें

इसलिए, एक भरी हुई इकाई को पुनः प्राप्त करना अपेक्षित लगता है।

2) जबकि T getOne(ID id)जावदोक कहता है (जोर मेरा है):

दिए गए पहचानकर्ता के साथ इकाई का संदर्भ देता है ।

वास्तव में, संदर्भ शब्दावली वास्तव में बोर्ड है और जेपीए एपीआई किसी भी getOne()विधि को निर्दिष्ट नहीं करता है ।
तो सबसे अच्छी बात यह समझने के लिए कि वसंत आवरण क्या लागू करता है:

@Override
public T getOne(ID id) {
    Assert.notNull(id, ID_MUST_NOT_BE_NULL);
    return em.getReference(getDomainClass(), id);
}

यहाँ em.getReference()एक EntityManagerविधि के रूप में घोषित किया गया है:

public <T> T getReference(Class<T> entityClass,
                              Object primaryKey);

और सौभाग्य से, EntityManagerjavadoc ने अपने इरादे को बेहतर बताया (जोर मेरा है):

एक उदाहरण प्राप्त करें, जिसका राज्य आलसी हो सकता है । यदि अनुरोधित उदाहरण डेटाबेस में मौजूद नहीं है, तो EntityNotFoundException को फेंक दिया जाता है जब उदाहरण स्थिति पहले एक्सेस की जाती है । (दृढ़ता प्रदाता रनटाइम को जब EntRNference कहा जाता है तो EntityNotFoundException को फेंकने की अनुमति दी जाती है।) आवेदन को यह उम्मीद नहीं करनी चाहिए कि उदाहरण स्थिति टुकड़ी पर उपलब्ध होगी , जब तक कि यह एप्लिकेशन द्वारा एक्सेस नहीं किया गया था जब तक कि इकाई प्रबंधक खुला नहीं था।

इसलिए, आह्वान करने से getOne()एक आलसी भ्रूण प्राप्त किया जा सकता है।
यहाँ, आलसी लाने वाले इकाई के रिश्तों को नहीं बल्कि इकाई को दर्शाता है।

इसका अर्थ है कि यदि हम आह्वान करते हैं getOne()और फिर दृढ़ता का संदर्भ बंद हो जाता है, तो इकाई को कभी भी लोड नहीं किया जा सकता है और इसलिए परिणाम वास्तव में अप्रत्याशित है।
उदाहरण के लिए, यदि प्रॉक्सी ऑब्जेक्ट धारावाहिक है, तो आप nullधारावाहिक परिणाम के रूप में एक संदर्भ प्राप्त कर सकते हैं या यदि कोई विधि प्रॉक्सी ऑब्जेक्ट पर लागू होती है, तो अपवाद जैसे LazyInitializationExceptionफेंक दिया जाता है।
तो इस तरह की स्थिति में, उस थ्रो को एक इंस्टेंस को हैंडल EntityNotFoundExceptionकरने के लिए उपयोग getOne()करने का मुख्य कारण है जो डेटाबेस में मौजूद नहीं है क्योंकि एक त्रुटि स्थिति का प्रदर्शन कभी नहीं किया जा सकता है, जबकि इकाई मौजूद नहीं है।

किसी भी स्थिति में, इसकी लोडिंग सुनिश्चित करने के लिए आपको सत्र को खोलते समय इकाई में हेरफेर करना होगा। आप इसे इकाई पर किसी भी विधि को लागू करके कर सकते हैं।
या findById(ID id)इसके बजाय एक बेहतर वैकल्पिक उपयोग ।


इतना अस्पष्ट एपीआई क्यों?

समाप्त करने के लिए, स्प्रिंग-डेटा-जेपीए डेवलपर्स के लिए दो प्रश्न:

  • क्यों नहीं के लिए एक स्पष्ट दस्तावेज होने getOne()? इकाई आलसी लोडिंग वास्तव में एक विस्तार नहीं है।

  • आपको getOne()लपेटने की आवश्यकता क्यों है EM.getReference()?
    बस लिपटे विधि से क्यों न चिपके getReference():? यह ईएम विधि वास्तव में बहुत खास है जबकि getOne() इतनी सरल प्रक्रिया को व्यक्त करती है।


3
मैं उलझन में था कि getOne () EntityNotFoundException को क्यों नहीं फेंक रहा है, लेकिन आपके "EntityNotFoundException को फेंक दिया जाता है जब इंस्टेंस स्टेट पहली बार एक्सेस होता है" मुझे कॉन्सेप्ट समझाया। धन्यवाद
TheCoder

इस उत्तर का सारांश: getOne()आलसी लोडिंग का उपयोग करता है, और EntityNotFoundExceptionयदि कोई वस्तु नहीं मिलती है तो फेंकता है। findById()सही लोड होता है, और नहीं मिलने पर अशक्त हो जाता है। चूंकि getOne () के साथ कुछ अप्रत्याशित परिस्थितियां हैं, इसके बजाय findById () का उपयोग करने की अनुशंसा की जाती है।
जनक मीणा

124

मूल अंतर यह है कि getOneआलसी भरी हुई है और findOneनहीं है।

निम्नलिखित उदाहरण पर विचार करें:

public static String NON_EXISTING_ID = -1;
...
MyEntity getEnt = myEntityRepository.getOne(NON_EXISTING_ID);
MyEntity findEnt = myEntityRepository.findOne(NON_EXISTING_ID);

if(findEnt != null) {
     findEnt.getText(); // findEnt is null - this code is not executed
}

if(getEnt != null) {
     getEnt.getText(); // Throws exception - no data found, BUT getEnt is not null!!!
}

1
आलसी लोडेड का मतलब यह नहीं है कि यह केवल तभी लोड होगा जब इकाई का उपयोग होने वाला है? इसलिए मुझे उम्मीद है कि getNnt शून्य और दूसरे के अंदर का कोड होगा यदि निष्पादित नहीं किया जा सकता है तो आप कृपया समझा सकते हैं। धन्यवाद!
डग

अगर एक कंप्लीटटेबल सिवनी के अंदर लपेटा हुआ है <> वेब सेवा मैंने पाया है कि आप आलसी कार्यान्वयन के कारण findOne () बनाम getOne () का उपयोग करना चाहते हैं।
फ्राट

76

1. गेटऑन (आईडी) विधि विफल क्यों होती है?

इस अनुभाग को डॉक्स में देखें । आप पहले से ही स्थान के लेन-देन को ओवरराइड कर रहे हैं जिससे समस्या उत्पन्न हो सकती है। हालाँकि, अधिक जानकारी के बिना यह उत्तर देना मुश्किल है।

2. मुझे गेटऑन (आईडी) पद्धति का उपयोग कब करना चाहिए?

स्प्रिंग डेटा जेपीए के इंटर्नल में खुदाई के बिना, इकाई को पुनः प्राप्त करने के लिए उपयोग किए जाने वाले तंत्र में अंतर प्रतीत होता है।

यदि आप देखें के तहत JavaDoc को भी देखें :getOne(ID)

See Also:
EntityManager.getReference(Class, Object)

ऐसा लगता है कि यह विधि सिर्फ जेपीए इकाई प्रबंधक के कार्यान्वयन को दर्शाती है।

हालाँकि, डॉक्स इसके लिए findOne(ID)उल्लेख नहीं करते हैं।

सुराग रिपॉजिटरी के नामों में भी है। JpaRepositoryजेपीए विशिष्ट है और इसलिए यदि आवश्यक हो तो इकाई प्रबंधक को कॉल सौंप सकते हैं। CrudRepositoryउपयोग की गई दृढ़ता प्रौद्योगिकी के अज्ञेय है। यहाँ देखो । यह जेपीए, Neo4J आदि जैसे कई दृढ़ता प्रौद्योगिकियों के लिए एक मार्कर इंटरफ़ेस के रूप में उपयोग किया जाता है ।

इसलिए आपके उपयोग के मामलों के लिए दो तरीकों में वास्तव में एक 'अंतर' नहीं है, यह सिर्फ इतना findOne(ID)है कि अधिक विशिष्ट से अधिक सामान्य है getOne(ID)। जो आप उपयोग करते हैं, वह आपके और आपकी परियोजना पर निर्भर करता है, लेकिन मैं व्यक्तिगत findOne(ID)रूप से इससे चिपके रहूंगा क्योंकि यह आपके कोड को कम क्रियान्वयन को विशिष्ट बनाता है और भविष्य में बहुत अधिक रिफैक्टिंग के बिना MongoDB आदि चीजों की ओर बढ़ने के दरवाजे खोलता है :)


थैंक यू डोनोवन, आपके जवाब में समझदारी है।
मैनुअल जॉर्डन

20
मुझे लगता है कि यह कहना बहुत भ्रामक है कि there's not really a 'difference' in the two methodsयहां, क्योंकि वास्तव में एक बड़ा अंतर है कि इकाई को कैसे पुनर्प्राप्त किया जाता है और आपको किस तरह से वापसी की उम्मीद करनी चाहिए। @Davidxxx द्वारा आगे के उत्तर को बहुत अच्छी तरह से रेखांकित किया गया है, और मुझे लगता है कि स्प्रिंग डेटा जेपीए का उपयोग करने वाले सभी को इसके बारे में पता होना चाहिए। अन्यथा, यह काफी सिरदर्द का कारण बन सकता है।
फ्रिडबर्ग

16

getOneतरीकों रिटर्न डीबी से केवल संदर्भ (आलसी लोड हो रहा है)। इसलिए मूल रूप से आप लेन-देन के बाहर हैं ( Transactionalआपको सेवा वर्ग में घोषित नहीं किया गया है), और त्रुटि उत्पन्न होती है।


EntityManager.getReference लगता है (क्लास, ऑब्जेक्ट) "कुछ भी नहीं" देता है क्योंकि हम एक नए लेन-देन के दायरे में हैं।
मैनुअल जॉर्डन

2

मुझे वास्तव में उपरोक्त उत्तरों से बहुत मुश्किल है। डिबगिंग के नजरिए से मैंने मूर्खतापूर्ण गलती को जानने के लिए लगभग 8 घंटे का समय दिया।

मेरे पास स्प्रिंग + हाइबरनेट + डोजर + मैसकल प्रोजेक्ट है। स्पष्ट होना।

मेरे पास उपयोगकर्ता इकाई, बुक एंटिटी है। आप मैपिंग की गणना करते हैं।

एक उपयोगकर्ता से जुड़ी कई पुस्तकें थीं। लेकिन UserServiceImpl में मैं getOne (userId) द्वारा इसे खोजने की कोशिश कर रहा था;

public UserDTO getById(int userId) throws Exception {

    final User user = userDao.getOne(userId);

    if (user == null) {
        throw new ServiceException("User not found", HttpStatus.NOT_FOUND);
    }
    userDto = mapEntityToDto.transformBO(user, UserDTO.class);

    return userDto;
}

बाकी परिणाम है

{
"collection": {
    "version": "1.0",
    "data": {
        "id": 1,
        "name": "TEST_ME",
        "bookList": null
    },
    "error": null,
    "statusCode": 200
},
"booleanStatus": null

}

उपर्युक्त कोड उन पुस्तकों को नहीं लाया गया है जो उपयोगकर्ता द्वारा पढ़ी जाती हैं।

GetLne (ID) के कारण बुकलिस्ट हमेशा शून्य था। FindOne (ID) में बदलने के बाद। परिणाम है

{
"collection": {
    "version": "1.0",
    "data": {
        "id": 0,
        "name": "Annama",
        "bookList": [
            {
                "id": 2,
                "book_no": "The karma of searching",
            }
        ]
    },
    "error": null,
    "statusCode": 200
},
"booleanStatus": null

}


-1

जबकि spring.jpa.open-in-view सच था, मुझे getOne से कोई समस्या नहीं थी, लेकिन इसे गलत पर सेट करने के बाद, मुझे LazyInitializationException मिली। तब findById के साथ बदलकर समस्या हल की गई थी।
यद्यपि गेटऑन पद्धति को प्रतिस्थापित किए बिना एक और समाधान है, और उस पद्धति पर @ ट्रान्सएक्टेंशनल डाला जाता है जिसे रिपॉजिटरी.गेटऑन (आईडी) कहा जाता है। इस तरह लेन-देन मौजूद रहेगा और सत्र आपके तरीके से बंद नहीं होगा और इकाई का उपयोग करते समय कोई भी LazyInitializationException नहीं होगी।


-2

मुझे एक समान समस्या यह समझने में थी कि क्यों JpaRespository.getOne (id) काम नहीं करता है और एक त्रुटि है।

मैं गया और JpaRespository.findById (id) में बदल गया, जिसके लिए आपको एक वैकल्पिक लौटना होगा।

यह शायद StackOverflow पर मेरी पहली टिप्पणी है।


दुर्भाग्य से, यह प्रश्न का उत्तर और उत्तर नहीं देता है, और न ही मौजूदा उत्तरों में सुधार करता है।
JSTL

मैं देखता हूं, कोई बात नहीं।
अक्षयमितल १४३
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.