CDI का उपयोग करते हुए Java EE अनुप्रयोगों में EntityManager का संदर्भ प्राप्त करना




jpa java-ee-7 (3)

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

1. एक अनुरोध सीडीआई बीन को स्कॉच करें, जिसमें EntityManager लिए एक संदर्भ है और बीन को सीडीआई EntityManager किया गया है।

import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@RequestScoped
public class RequestScopedBean {

    @PersistenceContext
    private EntityManager entityManager;

    public EntityManager getEntityManager() {
        return entityManager;
    }
}
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

@ApplicationScoped
public class ApplicationScopedBean {

    @Inject
    private RequestScopedBean requestScopedBean;

    public void persistEntity(Object entity) {
        requestScopedBean.getEntityManager().persist(entity);
    }
}

इस उदाहरण में एक EntityManager बनाया जाएगा जब RequestScopedBean बनाया गया है, और RequestScopedBean नष्ट हो जाता है जब बंद हो जाएगा। अब मैं इंस्ट्रक्शन को एब्जेक्ट क्लास से ले जा सकता हूं ताकि इसे ApplicationScopedBean से निकाल दिया जा सके।

2. एक निर्माता बनाएँ जो कि EntityManager का उदाहरण तैयार करता है, और फिर EntityManager उदाहरण को एक आवेदन में स्किड सीडीआई बीन से इंजेक्ट करें।

import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class EntityManagerProducer {

    @PersistenceContext
    @Produces
    @RequestScoped
    private EntityManager entityManager;
}
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;

@ApplicationScoped
public class ApplicationScopedBean {

    @Inject
    private EntityManager entityManager;

    public void persistEntity(Object entity) {
        entityManager.persist(entity);
    }
}

इस उदाहरण में हमारे पास एक EntityManager भी होगा जो हर HTTP अनुरोध बनाया गया है, लेकिन EntityManager बंद करने के बारे में क्या है? क्या HTTP अनुरोध संसाधित होने के बाद भी इसे बंद कर दिया जाएगा? मुझे पता है कि @PersistanceContext एनोटेशन कंटेनर-प्रबंधित @PersistanceContext इंजेक्ट करता है। इसका मतलब यह है कि जब एक ग्राहक बीन नष्ट हो जाता है, तो एक EntityManager को बंद कर दिया जाएगा। इस स्थिति में एक ग्राहक बीन क्या है? क्या यह ApplicationScopedBean , जो कभी भी तब तक नष्ट नहीं होगा जब तक अनुप्रयोग बंद हो जाता है, या यह EntityManagerProducer ? कोई सलाह?

मुझे पता है कि मैं आवेदन के बजाय एक स्टेटलेस ईजेबी का इस्तेमाल कर सकता हूं और फिर बस @PersistanceContext एनोटेशन द्वारा EntityManager को @PersistanceContext कर सकता @PersistanceContext , लेकिन यह बात नहीं है :)


आपके सीडीआई निर्माता के साथ आप लगभग सही हैं केवल एक चीज यह है कि आपको उत्पादक क्षेत्र के बजाय निर्माता विधि का उपयोग करना चाहिए।

यदि आप सीडीडीई कन्टेनर (ग्लासफ़िश 4.1 और वाइल्डफ़ी 8.2.0) के रूप में वेल्ड का उपयोग कर रहे हैं, तो आपके उत्पाद की @Produces दौरान इस अपवाद को एक उत्पादक क्षेत्र पर @PersistenceContext @Produces , @PersistenceContext और @PersistenceContext @Produces के संयोजन का उदाहरण:

org.jboss.weld.exceptions.DefinitionException: WELD-001502: संसाधन निर्माता क्षेत्र [क्वालिफायर के साथ संसाधन निर्माता फील्ड [EntityManager] [@ Any @Default] को घोषित कर दिया गया है [[बैकडएनेटोटेटेड फिल्ड] @ प्रोडक्ट्स @ रेवेस्टस्स्कोप @ पेरिसटेंस कॉन्टैक्ट कॉम। सोमैपैकेज.एन्टीटीएमएनेजर प्रोड्टर entityManager]] होना चाहिए @ निर्भर scoped

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

सीडीआई 1.2, खंड 3.7 संसाधन:

कंटेनर को @Dependent के अलावा अन्य संसाधनों के साथ संसाधनों का समर्थन करने की आवश्यकता नहीं है पोर्टेबल अनुप्रयोगों को @Dependent के अलावा किसी अन्य दायरे के साथ संसाधनों को परिभाषित नहीं करना चाहिए।

यह उद्धरण निर्माता क्षेत्र के बारे में था। एक संसाधन देखने के लिए एक निर्माता विधि का उपयोग पूरी तरह से कानूनी है:

public class EntityManagerProducer {

    @PersistenceContext    
    private EntityManager em;

    @Produces
    @RequestScoped
    public EntityManager getEntityManager() {
        return em;
    }
}

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

तकनीकी रूप से सही होने के लिए, अंतर्निहित और अज्ञात कंटेनर जो क्षेत्र में संसाधन इंजेक्शन करते हैं em पुरानी इकाई प्रबंधकों का पुन: उपयोग करने की अनुमति है (जेपीए 2.1 विनिर्देश में खंड देखें, अनुभाग "7.9.1 कंटेनर ज़िम्मेदारियां", पेज 357)। लेकिन अभी तक, हम जेपीए द्वारा आवश्यक प्रोग्रामिंग मॉडल का सम्मान करते हैं।

पूर्ववर्ती उदाहरण में, यदि आप EntityManagerProducer @Dependent या @RequestScoped को चिह्नित करते हैं, तो इससे कोई फर्क नहीं पड़ेगा। @ निर्भर का उपयोग अर्थपूर्ण रूप से अधिक सही है। लेकिन अगर आप निर्माता वर्ग के अनुरोध के दायरे से अधिक व्यापक दायरे डालते हैं तो आप कई थ्रेडों के अंतर्निहित इकाई प्रबंधक संदर्भ को उजागर करने का जोखिम लेते हैं, जो हम दोनों जानते हैं कि यह करना अच्छी बात नहीं है। अंतर्निहित संस्था प्रबंधक कार्यान्वयन शायद एक थ्रेड-स्थानीय ऑब्जेक्ट है, लेकिन पोर्टेबल एप्लिकेशन कार्यान्वयन विवरण पर भरोसा नहीं कर सकते।

सीडीआई यह नहीं जानता कि आप किस अनुरोध को सीमित संदर्भ में डालते हैं। और कुछ और की तुलना में, एक कंटेनर-प्रबंधित इकाई प्रबंधक को एप्लिकेशन कोड द्वारा बंद नहीं करना चाहिए।

जेपीए 2.1, अनुभाग "7.9.1 कंटेनर जिम्मेदारियां":

यदि कंटेनर-प्रबंधित एंटिटी मैनेजर पर एंटिटी मैनेजर.कोज़ को कॉल किया जाता है तो कंटेनर को अवैध रूप से अवक्षेपित करना चाहिए।

दुर्भाग्यवश, कई लोग कंटेनर-प्रबंधित इकाई प्रबंधक को बंद करने के लिए एक @Disposes विधि का उपयोग करते हैं ओरेकल द्वारा प्रदान की जाने वाली आधिकारिक जावा ईई 7 ट्यूटोरियल के साथ-साथ सीडीआई विनिर्देश ही कंटेनर-प्रबंधित एंटिटी मैनेजर को बंद करने के लिए एक डिस्पोजर का इस्तेमाल करने पर उन्हें कौन दोषी ठहरा सकता है। यह बस गलत है और EntityManager.close() लिए कॉल एक IllegalStateException से फेंक देगा, चाहे आप उस कॉल को किसी डिस्पोजर विधि में या किसी और स्थान में डाल दें। ओरेकल का उदाहरण उत्पादक वर्ग को @javax.inject.Singleton घोषित करके दो की सबसे बड़ी पापी है। जैसा कि हमने सीखा है, इस खतरे को अंतर्निहित इकाई प्रबंधक को कई अलग-अलग धागे के संदर्भ में उजागर करना है।

यह सिद्ध हो चुका है कि सीडीआई उत्पादकों और डिपार्टर्स का गलत तरीके से उपयोग करके, 1) थ्रेड-सुरक्षित इकाई प्रबंधक को कई धागे में लीक नहीं किया जा सकता है और 2) डिस्पोजर का कोई प्रभाव नहीं है; इकाई प्रबंधक खुला छोड़कर क्या हुआ IllegalStateException जो कंटेनर निगलने का कोई निशान नहीं छोड़ रहा है (एक रहस्यमय लॉग प्रविष्टि है जो कहती है कि "एक उदाहरण को नष्ट करने में त्रुटि") थी।

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

कंटेनर-प्रबंधित एंटिटी मैनेजर को कैसे प्राप्त करें और एंटिटी मैनेजर्स को देखने के लिए सीडीआई का उपयोग कैसे करें, पर पूरी तस्वीर प्राप्त करने के लिए, आप इसे पढ़ना चाह सकते हैं और यह


आप जानबूझकर EntityManagerFactory इंजेक्शन कर सकते हैं, यह धागा बचा है

@PersistenceUnit(unitName = "myUnit")
private EntityManagerFactory entityManagerFactory;

तो आप entityManagerFactory से EntityManager retrive कर सकते हैं


आपको नीचे दिए गए उदाहरण के रूप में EntityManager को बंद करने के लिए @Dispose व्याख्या एनोटेशन का उपयोग करना चाहिए:

@ApplicationScoped
public class Resources {

    @PersistenceUnit
    private EntityManagerFactory entityManagerFactory;

    @Produces
    @Default
    @RequestScoped
    public EntityManager create() {
        return this.entityManagerFactory.createEntityManager();
    }

    public void dispose(@Disposes @Default EntityManager entityManager) {
        if (entityManager.isOpen()) {
            entityManager.close();
        }
    }

}






java-ee-7