2013-02-27 7 views
19

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

हमारे पास हमारे सभी क्षेत्रों के लिए ऑनलाइन संपादन है, ताकि जब उपयोगकर्ता एक फ़ील्ड संपादित करता है और पुष्टि करता है, तो एक आईडी और संशोधित फ़ील्ड नियंत्रक को जेसन के रूप में पास कर दिया जाता है। नियंत्रक क्लाइंट से किसी भी फ़ील्ड (1 से n) में लेने और केवल उन फ़ील्ड को अपडेट करने में सक्षम होना चाहिए।

जैसे, आईडी == 1 के साथ एक उपयोगकर्ता अपने DISPLAYNAME संपादित करता है तो डेटा सर्वर पर पोस्ट इस तरह दिखता है:

{"id":"1", "displayName":"jim"} 

वर्तमान में, हम UserController में एक अधूरी समाधान के रूप में नीचे उल्लिखित:

@RequestMapping(value = "/{id}", method = RequestMethod.POST) 
public @ResponseBody ResponseEntity<User> update(@RequestBody User updateUser) { 
    dbUser = userRepository.findOne(updateUser.getId()); 
    customObjectMerger(updateUser, dbUser); 
    userRepository.saveAndFlush(updateUuser); 
    ... 
} 

कोड यहाँ काम करता है, लेकिन कुछ मुद्दे हैं: @RequestBody एक नया updateUser बनाता है, id और displayName में भरता है। CustomObjectMerger डेटाबेस से संबंधित के साथ updateUser विलय करता है, जो updateUser में शामिल फ़ील्ड को अपडेट करता है।

समस्या यह है कि वसंत मूलभूत मूल्यों और अन्य स्वतः निर्मित फ़ील्ड मान है, जो, विलय की बात है, मान्य डेटा हम dbUser में है कि अधिलेखित कर देता है साथ updateUser में कुछ फ़ील्ड का निर्माण है। स्पष्ट रूप से यह घोषणा करते हुए कि इन क्षेत्रों को अनदेखा करना एक विकल्प नहीं है, क्योंकि हम इन क्षेत्रों को सेट करने में सक्षम होने के लिए हमारे update चाहते हैं।

मैं किसी तरह से में देख रहा हूँ वसंत स्वचालित रूप से (डिफ़ॉल्ट/ऑटो फ़ील्ड मान को रीसेट किए बिना) केवल जानकारी स्पष्ट रूप से dbUser में update() समारोह में भेजा विलय है। क्या ऐसा करने का कोई आसान तरीका है?

अद्यतन: मैंने पहले से ही निम्न विकल्प माना है जो मैं पूछ रहा हूं, लेकिन काफी नहीं। समस्या यह है कि यह @RequestParam के रूप में अद्यतन डेटा लेता है और (AFAIK) JSON तार नहीं करता है कि:

//load the existing user into the model for injecting into the update function 
@ModelAttribute("user") 
public User addUser(@RequestParam(required=false) Long id){ 
    if (id != null) return userRepository.findOne(id); 
    return null; 
} 
.... 
//method declaration for using @MethodAttribute to pre-populate the template object 
@RequestMapping(value = "/{id}", method = RequestMethod.POST) 
public @ResponseBody ResponseEntity<User> update(@ModelAttribute("user") User updateUser){ 
.... 
} 

मैं माना जाता है फिर से लिख मेरी customObjectMerger() JSON के साथ और अधिक उचित रूप से काम करने के लिए, गिनती और इसे लेने के होने विचार में केवल HttpServletRequest से आने वाले फ़ील्ड। लेकिन पहली जगह में customObjectMerger() का उपयोग करने के लिए भी हैकी लगता है जब वसंत लगभग बिल्कुल ठीक है जो मैं देख रहा हूं, कमजोर JSON कार्यक्षमता से कम है। यदि कोई जानता है कि स्प्रिंग को ऐसा करने के लिए कैसे जाना है, तो मैं इसकी सराहना करता हूं!

+0

@ सैमसेला - क्या आपको कभी भी बेहतर समाधान मिल गया है, विशेष रूप से ऐसी स्थिति के लिए जहां घोंसला वाली वस्तुएं हैं? मेरे पास एक ही समस्या है, और इस पोस्ट को पढ़ने से पहले, आपके जैसा कुछ किया .. यदि आपके पास समय है तो कृपया देखें: http://stackoverflow.com/questions/16473727/spring-3-ajax-post-request-with -requestbody-and-modelattribute-and-sessionatt – arcseldon

उत्तर

18

मैं सिर्फ यह एक ही समस्या में पड़ गए हैं। मेरा वर्तमान समाधान इस तरह दिखता है। मैंने अभी तक बहुत अधिक परीक्षण नहीं किया है, लेकिन प्रारंभिक निरीक्षण पर यह काफी अच्छी तरह से काम कर रहा है।

@Autowired ObjectMapper objectMapper; 
@Autowired UserRepository userRepository; 

@RequestMapping(value = "/{id}", method = RequestMethod.POST) 
public @ResponseBody ResponseEntity<User> update(@PathVariable Long id, HttpServletRequest request) throws IOException 
{ 
    User user = userRepository.findOne(id); 
    User updatedUser = objectMapper.readerForUpdating(user).readValue(request.getReader()); 
    userRepository.saveAndFlush(updatedUser); 
    return new ResponseEntity<>(updatedUser, HttpStatus.ACCEPTED); 
} 

ObjectMapper प्रकार org.codehaus.jackson.map.ObjectMapper की एक सेम है।

आशा इस कोई मदद करता है,

संपादित करें:

बच्चे वस्तुओं में समस्याएं आती हैं है। यदि किसी बच्चे ऑब्जेक्ट को आंशिक रूप से अपडेट करने के लिए कोई प्रॉपर्टी प्राप्त होती है तो यह एक ताजा ऑब्जेक्ट बनाएगी, उस प्रॉपर्टी को अपडेट करेगी, और इसे सेट करेगी। यह उस ऑब्जेक्ट पर अन्य सभी गुणों को मिटा देता है। अगर मैं एक साफ समाधान में आता हूं तो मैं अपडेट करूंगा।

+0

धन्यवाद टायलर, यह एक आसान समाधान की तरह लगता है। मुझे अभी भी आश्चर्य है कि क्या पीएसटी डेटा से जेएसओएन के प्रत्यक्ष/स्वचालित बाध्यकारी को एक स्थिर वस्तु तक अनुमति देने के लिए स्प्रिंग फ़ंक्शन है या नहीं। – SamEsla

+0

समाधान के लिए धन्यवाद। मैंने सोचा कि इस स्थिति को हल करने का एकमात्र तरीका मैन्युअल रूप से प्रसंस्करण कर रहा है। पहले कभी जैक्सन ऑब्जेक्टमैपर नहीं जाना है ... –

+0

@ टाइलर - क्या आपको कभी भी बेहतर समाधान मिल गया है, विशेष रूप से ऐसी स्थिति के लिए जहां घोंसला वाली वस्तुएं हैं? मेरे पास एक ही समस्या है, और इस पोस्ट को पढ़ने से पहले, ओपी के समान कुछ किया। आपके पास समय हो तो कृपया देखें: http://stackoverflow.com/questions/16473727/spring-3-ajax-post-request-with-requestbody-and-modelattribute-and-sessionatt – arcseldon

0

मुख्य समस्या अपने निम्न कोड में निहित है:

@RequestMapping(value = "/{id}", method = RequestMethod.POST) 
public @ResponseBody ResponseEntity<User> update(@RequestBody User updateUser) { 
    dbUser = userRepository.findOne(updateUser.getId()); 
    customObjectMerger(updateUser, dbUser); 
    userRepository.saveAndFlush(updateUuser); 
    ... 
} 

ऊपर कार्यों में, आप अपने निजी कार्यों & वर्गों (UserRepository, customObjectMerger, ...) में से कुछ फोन है, लेकिन कोई स्पष्टीकरण देने के कैसे यह काम करता है या वे कार्य कैसे दिखते हैं।इसलिए मैं केवल अनुमान लगा सकते हैं:

CustomObjectMerger डेटाबेस से इसी dbUser के साथ इस updateUser विलीन हो जाती है, को अद्यतन करने के केवल क्षेत्रों updateUser में शामिल थे।

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

1) सर्वर से अपरिवर्तित गुण सहित सभी गुण() भेजा जा रहा है:

अपने मामले में 2 विकल्प हैं। इससे थोड़ा और बैंडविड्थ खर्च हो सकता है, लेकिन आप अभी भी अपना रास्ता

2) आपको उपयोगकर्ता ऑब्जेक्ट के लिए डिफ़ॉल्ट मान के रूप में कुछ विशेष मान सेट करना चाहिए (उदाहरण के लिए, आईडी = -1, आयु = -1 ...)। फिर customObjectMerger में आप केवल उस मान को सेट करें जो -1 नहीं है।

यदि आपको लगता है 2 ऊपर समाधान संतुष्ट नहीं हैं, पार्स करने json खुद का अनुरोध पर विचार करें, और स्प्रिंग वस्तु मानचित्रण तंत्र के साथ परेशान नहीं है। कभी-कभी यह बहुत उलझन में पड़ता है।

+0

हाय होआंग, आपके उत्तर के लिए धन्यवाद। 'अनुरोधबॉडी 'मेरे पेलोड को' उपयोगकर्ता 'ऑब्जेक्ट के नए उदाहरण में बदल देता है। निजी तरीकों के अंदरूनी सवाल हाथ में सवाल के लिए महत्वपूर्ण नहीं हैं। कृपया मार्टिन फे के जवाब और मेरे अपडेट को देखने के लिए देखें कि कैसे '@ ModelAttribute' उसी कार्यक्षमता को पूरा करने के लिए काम करता है जिसे मैं ढूंढ रहा हूं, लेकिन अनुरोध में आने वाले' अनुरोधपाराम्स 'के साथ काम करता है। इस विधि का उपयोग करके, स्प्रिंग स्वचालित रूप से 'RequestParams' से नई जानकारी को डेटाबेस से प्राप्त होने वाली मौजूदा ऑब्जेक्ट से जोड़ती है। मैं जेएसओएन पेलोड के साथ ऐसा करने के लिए स्प्रिंग डॉक्स को स्कोअर कर रहा हूं। – SamEsla

+0

@ सैमसेला: मैं फेय की विधि और आपका प्रश्न समझता हूं। मेरी राय में, फे का रास्ता स्मार्ट है, लेकिन यह काफी अच्छा अभ्यास नहीं है। मॉडल एट्रिब्यूट के साथ काम करते समय मुझे स्वयं को कुछ समस्याएं आती हैं, खासकर, जैसा कि उन्होंने टिप्पणी की है, यह नियंत्रक में डीएओ ऑब्जेक्ट के साथ काम कर रहा है। –

+0

@ सैमसेला: ओह, मैंने आपके प्रश्न को गलत तरीके से पढ़ा। मैंने RequestBody लिखा लेकिन किसी भी तरह से मुझे लगता है कि आप ResponseBody का मतलब है। मेरी आंखें कितनी खराब हैं @ _ @ मैंने उस अप्रासंगिक भाग को हटाने के लिए उत्तर अपडेट किया है। –

1

हम @ModelAttribute उपयोग कर रहे हैं कि आप क्या करना चाहते हैं हासिल करने के लिए।

  • एक विधि @ modelattribute साथ एनोटेट जो भंडार throguh एक pathvariable के आधार पर एक उपयोगकर्ता को लोड करता है बनाएँ।

  • एक विधि का कोई पैरामीटर @modelattribute

यहां मुद्दा यह है कि @modelattribute विधि मॉडल के लिए प्रारंभकर्ता है साथ @Requestmapping पैदा करते हैं। फिर वसंत इस मॉडल के साथ अनुरोध विलीन कर देता है क्योंकि हम इसे @requestmapping विधि में घोषित करते हैं।

यह आपको आंशिक अद्यतन कार्यक्षमता देता है।

कुछ, या यहाँ तक कि एक बहुत? ;) तर्क देगा कि यह वैसे भी खराब अभ्यास है क्योंकि हम सीधे हमारे नियंत्रक में हमारे डीएओ का उपयोग करते हैं और समर्पित सेवा परत में यह विलय नहीं करते हैं। लेकिन वर्तमान में हम इस अपमान के कारण मुद्दों में भाग नहीं पाए।

+0

मार्टिन धन्यवाद, लेकिन मैंने पहले ही यह कोशिश की है। इस विधि के साथ समस्या यह है कि फ़ील्ड को अपडेट करने के लिए केवल '@ RequestParam' में ही लगता है। हम आपके द्वारा उल्लिखित इस सटीक कार्यक्षमता को डुप्लिकेट करने का एक तरीका चाहते हैं, लेकिन इनपुट के रूप में JSON के साथ काम करना चाहते हैं। मुझे यकीन है कि इस जगह के लिए स्प्रिंग में अंतर्निहित कार्यक्षमता है, लेकिन मैंने अभी तक इसमें भाग नहीं लिया है। – SamEsla

+0

यह समान काम करना चाहिए।सच है कि हम वर्तमान में पोस्ट डेटा के साथ इसका उपयोग करते हैं लेकिन मैं इसे जेसन ऑब्जेक्ट के साथ करने की कोशिश करूंगा। अंत में वसंत इस तरह के समान संभाल रहा है। आपके मॉडल अंशदान से मेल खाने वाला कुछ भी (requestparam, json props) विलय किया जाना चाहिए। आईपैड एटीएम पर आईएम के बाद कल मैं एक छोटा सा उदाहरण करने की कोशिश करूंगा। –

+1

मेरे उत्तर के लिए 3 सप्ताह से अधिक देर हो चुकी है .. लेकिन अंत में मेरे पास थोड़ा सा जांच करने के लिए कुछ समय था। होआंग लांग सही है। जेसन अनुरोधबोडी को संभालने का कोई क्लीनर तरीका नहीं है। ऐसा लगता है कि हम एक अनुरोधकर्ता विलय खो रहे हैं :) वसंत अनुरोध हो सकता है? –

0

आंशिक अद्यतन @SessionAttributes कार्यक्षमता का उपयोग कर हल किया जा सकता है, जो customObjectMerger के साथ आपने स्वयं किया है। मेरा उत्तर यहाँ, विशेष रूप से संपादन, आप प्राप्त करने के लिए

देखो शुरू कर दिया:

https://stackoverflow.com/a/14702971/272180

1

मैं एक एपीआई कि कॉल persiste से पहले संस्थाओं के साथ दृश्य वस्तुओं मर्ज या विलय या अद्यतन का निर्माण।

यह पहला संस्करण है लेकिन मुझे लगता है कि यह एक शुरुआत है।

बस फिर अपने POJO`S क्षेत्रों में एनोटेशन UIAttribute का उपयोग का उपयोग करें:

MergerProcessor.merge(pojoUi, pojoDb);

यह देशी गुण और संग्रह के साथ काम करता है।

Git: https://github.com/nfrpaiva/ui-merge

1

बाद दृष्टिकोण में इस्तेमाल किया जा सकता है।

इस परिदृश्य के लिए, इकाई विधि आंशिक रूप से अपडेट होने के बाद से पैच विधि अधिक उपयुक्त होगी।

नियंत्रक विधि में, अनुरोध निकाय को स्ट्रिंग के रूप में लें।

उस स्ट्रिंग को JSONObject में कनवर्ट करें। फिर आने वाले डेटा के साथ चाबियों को फिर से भरें और मिलान करने वाले चर को अपडेट करें। इस दृष्टिकोण का

import org.json.JSONObject; 

@RequestMapping(value = "/{id}", method = RequestMethod.PATCH) 
public ResponseEntity<?> updateUserPartially(@RequestBody String rawJson, @PathVariable long id){ 

    dbUser = userRepository.findOne(id); 

    JSONObject json = new JSONObject(rawJson); 

    Iterator<String> it = json.keySet().iterator(); 
    while(it.hasNext()){ 
     String key = it.next(); 
     switch(key){ 
      case "displayName": 
       dbUser.setDisplayName(json.get(key)); 
       break; 
      case "....": 
       .... 
     } 
    } 
    userRepository.save(dbUser); 
    ... 
} 

नकारात्मक आप मैन्युअल रूप से भेजे मूल्यों को मान्य करने के लिए है, है।

+0

हाय वहाँ! जवाब के लिए धन्यवाद। हाँ, हिंडसाइट पाच में प्राकृतिक समाधान की तरह लगता है, लेकिन मुझे लगता है कि हम में से कोई भी इसे मानता नहीं है क्योंकि इसे संस्करण 3.2 में नई स्प्रिंग फ्रेमवर्क कार्यक्षमता के रूप में कुछ महीने पहले लागू किया गया था। – SamEsla

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