2015-03-03 10 views
5

के साथ स्ट्रीम क्लोजेबल संसाधन this article पढ़ने के बाद, मैं स्प्रिंग का उपयोग डेटाबेस क्वेरी परिणामों को सीधे जेएसओएन प्रतिक्रिया में स्ट्रीम करना चाहता हूं ताकि निरंतर स्मृति उपयोग सुनिश्चित हो सके (List स्मृति में कोई लालची लोडिंग नहीं)।स्प्रिंग एमवीसी

हाइबरनेट के साथ आलेख में किए गए कार्यों के समान, मैंने greetingRepository ऑब्जेक्ट को इकट्ठा किया जो JdbcTemplate पर आधारित डेटाबेस सामग्री की एक स्ट्रीम देता है। कि कार्यान्वयन में, मैं पूछे ResultSet पर एक इटरेटर बनाते हैं, और मैं धारा वापसी इस प्रकार है:

return StreamSupport.stream(spliterator(), false).onClose(() -> { 
    log.info("Closing ResultSetIterator stream"); 
    JdbcUtils.closeResultSet(resultSet); 
}); 
एक onClose() विधि गारंटी है कि अंतर्निहित ResultSet यदि धारा एक try-with-resources निर्माण में घोषित किया जाता है बंद कर दिया जाएगा साथ

यानी :

try(Stream<Greeting> stream = greetingRepository.stream()) { 
    // operate on the stream 
} // ResultSet underlying the stream will be guaranteed to be closed 

लेकिन लेख में के रूप में, मैं इस धारा एक कस्टम वस्तु नक्शाकार (बढ़ाया MappingJackson2HttpMessageConverter लेख में परिभाषित) द्वारा उनका उपयोग हो करना चाहते हैं। अगर हम try-with-resources अलग की जरूरत ले, यह संभव इस प्रकार है:

@RequestMapping(method = GET) 
Stream<GreetingResource> stream() { 
    return greetingRepository.stream().map(GreetingResource::new); 
} 

हालांकि के रूप में एक सहयोगी है कि लेख के नीचे टिप्पणी की, यह नहीं देखभाल अंतर्निहित संसाधनों को बंद करने के ले करता है।

स्प्रिंग एमवीसी के संदर्भ में, मैं जेएसओएन प्रतिक्रिया में डेटाबेस से कैसे स्ट्रीम कर सकता हूं और अभी भी गारंटी देता हूं कि ResultSet बंद हो जाएगा? क्या आप एक ठोस उदाहरण समाधान प्रदान कर सकते हैं?

+2

मुझे नहीं लगता कि आपकी समस्या संसाधन रिसाव है: वसंत निश्चित रूप से लेनदेन को प्रतिबद्ध करेगा और कनेक्शन को जारी करेगा, आपके परिणाम सेट को पारस्परिक रूप से बंद कर देगा। लेकिन मैं विपरीत समस्या की अपेक्षा करता हूं: आप कैसे प्रबंधित करते हैं कि कनेक्शन दृश्य परत में रहता है? मैं इसके लिए 'OpenSessionInViewInterceptor' पर भरोसा करता हूं, जो हाइबरनेट-विशिष्ट है। –

+0

ठीक है, मेरे परीक्षण परिदृश्य में मैं लेनदेन का उपयोग करना भूल गया, उनके साथ मैं अब दृश्य परत पर स्ट्रीम नहीं कर सकता (इसके अतिरिक्त मैं MySQL का उपयोग करता हूं, इसलिए मैं भाग्य से बाहर हूं)। मैंने निष्कर्ष निकाला है कि दृश्य परत पर स्ट्रीमिंग संभावित रूप से कुशल और काफी सुरुचिपूर्ण के रूप में आकर्षक है, लेकिन अभ्यास में उपयोग करने के लिए दुखद रूप से अभी भी मुश्किल है। –

+0

दुर्भाग्य से, इसके लिए अभी भी अपर्याप्त समर्थन है। यह जंगली सीमा है। मुझे उम्मीद है कि यह पकड़ लेगा, हालांकि, जावा आर्किटेक्चर में अब तक इस विभाग में बहुत कमी आई है। –

उत्तर

0

आप धारावाहिक समय पर क्वेरी निष्पादन को रोकने के लिए एक निर्माण बना सकते हैं। यह निर्माण लेनदेन प्रोग्रामेटिकल शुरू और समाप्त करेगा।

public class TransactionalStreamable<T> { 

    private final PlatformTransactionManager platformTransactionManager; 

    private final Callable<Stream<T>> callable; 

    public TransactionalStreamable(PlatformTransactionManager platformTransactionManager, Callable<Stream<T>> callable) { 
     this.platformTransactionManager = platformTransactionManager; 
     this.callable = callable; 
    } 

    public Stream stream() { 
     TransactionTemplate txTemplate = new TransactionTemplate(platformTransactionManager); 
     txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
     txTemplate.setReadOnly(true); 

     TransactionStatus transaction = platformTransactionManager.getTransaction(txTemplate); 

     try { 
      return callable.call().onClose(() -> { 
       platformTransactionManager.commit(transaction); 
      }); 
     } catch (Exception e) { 
      platformTransactionManager.rollback(transaction); 
      throw new RuntimeException(e); 
     } 
    } 

    public void forEach(Consumer<T> c) { 
     try (Stream<T> s = stream()){ 
      s.forEach(c); 
     } 
    } 
} 

एक समर्पित json serializer का उपयोग करना:

JsonSerializer<?> transactionalStreamableSer = new StdSerializer<TransactionalStreamable<?>>(TransactionalStreamable.class, true) { 
    @Override 
    public void serialize(TransactionalStreamable<?> streamable, JsonGenerator jgen, SerializerProvider provider) throws IOException { 
     jgen.writeStartArray(); 
     streamable.forEach((CheckedConsumer) e -> { 
      provider.findValueSerializer(e.getClass(), null).serialize(e, jgen, provider); 
     }); 

     jgen.writeEndArray(); 
    } 
}; 

कौन इस तरह इस्तेमाल किया जा सकता:

@RequestMapping(method = GET) 
TransactionalStreamable<GreetingResource> stream() { 
    return new TransactionalStreamable(platformTransactionManager ,() -> greetingRepository.stream().map(GreetingResource::new)); 
} 

सभी काम हो जाएगा जब जैक्सन वस्तु को क्रमानुसार होगा। यह त्रुटि प्रबंधन (उदाहरण के लिए नियंत्रक सलाह का उपयोग कर) के बारे में कोई समस्या हो सकती है या नहीं।