2013-02-25 62 views
10

में जेपीए के साथ EntityManager थ्रेडलोकल पैटर्न मैं स्ट्रैट्स 1.3 + जेपीए (हाइबरनेट के साथ दृढ़ता प्रदाता के रूप में) का उपयोग करके एक सरल "बुक स्टोर" प्रोजेक्ट विकसित कर रहा हूं। मैं वसंत या किसी अन्य परिष्कृत विकास पर्यावरण (उदाहरण के लिए, जेबॉस) पर स्विच नहीं कर सकता हूं और मैं किसी भी हाइबरनेट-विशिष्ट तकनीक का उपयोग नहीं कर सकता (उदा।, Session कक्षा)।जेएसई

इस तथ्य को देखते हुए कि मैं जेएसई पर्यावरण में हूं, मुझे पूरी इकाई प्रबंधक की जीवन चक्र को स्पष्ट रूप से प्रबंधित करने की आवश्यकता है।

Book इकाई के रूप में परिभाषित किया गया है इस प्रकार है:

@Entity 
public class Book { 

@Id private String isbn; 
private String title; 
private Date publishDate; 

    // Getters and Setters 
} 

मैं तीन Action कक्षाएं, जो जिम्मेदार हैं, क्रमशः,, सभी पुस्तक उदाहरणों को पुन: प्राप्त इसका ISBN द्वारा किसी एक पुस्तक उदाहरण पुन: प्राप्त करने और एक अलग किताब विलय की परिभाषित डीबी में

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

public class ListBookAction extends Action { 

    private BookDAO dao = new BookDAO(); 

    @Override 
    public ActionForward execute(ActionMapping mapping, ActionForm form, 
      HttpServletRequest request, HttpServletResponse response) 
      throws Exception { 

     // Retrieve all the books 
     List<Book> books = dao.findAll(); 

     // Save the result set 
     request.setAttribute("books", books); 

     // Forward to the view 
     return mapping.findForward("booklist"); 
    } 

} 

BookDAO वस्तु किसी भी आपरेशन करने के लिए एक EntityManager उदाहरण का उपयोग करने की जरूरत है: उदाहरण के लिए, ListBookAction इस प्रकार परिभाषित किया गया है। यह देखते हुए कि EntityManger धागा सुरक्षित नहीं है, मैं BookUnitSession नामक एक सहायक वर्ग है जो एक ThreadLocal चर के भीतर EntityManager समाहित शुरू की:

public class BookUnitSession { 

    private static EntityManagerFactory emf = Persistence.createEntityManagerFactory("BookStoreUnit"); 
    private static final ThreadLocal<EntityManager> tl = new ThreadLocal<EntityManager>(); 

    public static EntityManager getEntityManager() { 
     EntityManager em = tl.get(); 

     if (em == null) { 
      em = emf.createEntityManager(); 
      tl.set(em); 
     } 
     return em; 
    } 

} 

सब कुछ काम करने के लिए लगता है, लेकिन मैं अभी भी थोड़े चिंतित हैं। अर्थात्:

  1. क्या यह समाधान सबसे अच्छा काम है? इस मामले में सबसे अच्छा अभ्यास कौन सा है?
  2. मुझे अभी भी EntityManager और EntityManagerFactory दोनों को स्पष्ट रूप से बंद करने की आवश्यकता है। मैं उसे कैसे कर सकता हूँ?

धन्यवाद

उत्तर

23

पिछले कुछ दिनों के दौरान मैं एक संभव समाधान बनाया गया है।

public class EntityManagerHelper { 

    private static final EntityManagerFactory emf; 
    private static final ThreadLocal<EntityManager> threadLocal; 

    static { 
     emf = Persistence.createEntityManagerFactory("BookStoreUnit");  
     threadLocal = new ThreadLocal<EntityManager>(); 
    } 

    public static EntityManager getEntityManager() { 
     EntityManager em = threadLocal.get(); 

     if (em == null) { 
      em = emf.createEntityManager(); 
      threadLocal.set(em); 
     } 
     return em; 
    } 

    public static void closeEntityManager() { 
     EntityManager em = threadLocal.get(); 
     if (em != null) { 
      em.close(); 
      threadLocal.set(null); 
     } 
    } 

    public static void closeEntityManagerFactory() { 
     emf.close(); 
    } 

    public static void beginTransaction() { 
     getEntityManager().getTransaction().begin(); 
    } 

    public static void rollback() { 
     getEntityManager().getTransaction().rollback(); 
    } 

    public static void commit() { 
     getEntityManager().getTransaction().commit(); 
    } 
} 

इस तरह के एक वर्ग सुनिश्चित करता है कि प्रत्येक थ्रेड (अर्थात, प्रत्येक अनुरोध) अपनी ही EntityManager उदाहरण मिल जाएगा: क्या मैं BookUnitSession वर्ग के साथ निर्माण करने के लिए कोशिश कर रहा था वास्तव में EntityManagerHelper वर्ग था। नतीजतन, प्रत्येक डीएओ वस्तु EntityManagerHelper.getEntityManager()

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

public class EntityManagerInterceptor implements Filter { 

    @Override 
    public void destroy() {} 

    @Override 
    public void init(FilterConfig fc) throws ServletException {} 

    @Override 
    public void doFilter(ServletRequest req, ServletResponse res, 
      FilterChain chain) throws IOException, ServletException { 

      try { 
       EntityManagerHelper.beginTransaction(); 
       chain.doFilter(req, res); 
       EntityManagerHelper.commit(); 
      } catch (RuntimeException e) { 

       if (EntityManagerHelper.getEntityManager() != null && EntityManagerHelper.getEntityManager().isOpen()) 
        EntityManagerHelper.rollback(); 
       throw e; 

      } finally { 
       EntityManagerHelper.closeEntityManager(); 
      } 
    } 
} 

यह दृष्टिकोण भी देखें की अनुमति देता है (जैसे, एक JSP पेज) यूनिट की खेतों लाने के लिए भले ही वे में आलसी प्रारंभ (ओपन सत्र किया गया है पैटर्न देखें)। एक जेएसई पर्यावरण में EntityManagerFactory को सर्वलेट कंटेनर बंद होने पर स्पष्ट रूप से बंद करने की आवश्यकता है।यह एक ServletContextListener वस्तु का उपयोग करके किया जा सकता है:

public class EntityManagerFactoryListener implements ServletContextListener { 

    @Override 
    public void contextDestroyed(ServletContextEvent e) { 
     EntityManagerHelper.closeEntityManagerFactory(); 
    } 

    @Override 
    public void contextInitialized(ServletContextEvent e) {} 

} 

web.xml तैनाती वर्णनकर्ता:

<listener> 
    <description>EntityManagerFactory Listener</description> 
    <listener-class>package.EntityManagerFactoryListener</listener-class> 
</listener> 

<filter> 
    <filter-name>interceptor</filter-name> 
    <filter-class>package.EntityManagerInterceptor</filter-class> 
</filter> 

<filter-mapping> 
    <filter-name>interceptor</filter-name> 
    <url-pattern>*.do</url-pattern> 
</filter-mapping> 
+0

अच्छा काम, लेकिन आप शायद जेएसई के बजाय जेईई का मतलब है। –

+1

धन्यवाद। मैं वर्तमान में बिना किसी एप्लिकेशन सर्वर के टॉमकैट 7 और स्ट्रूट्स 1.3 का उपयोग कर रहा हूं। इसलिए, मैं कहूंगा कि मैं पूरी तरह से जेईई अनुपालन नहीं कर रहा हूं। दृढ़ता के दृष्टिकोण से, यह एक जेएसई पर्यावरण में होना पसंद है। यही कारण है कि मैंने शीर्षक में जेएसई को बताया। –

+0

हाय, क्या यह समाधान अंत में काम करता है? मैंने थ्रेडलोकल उपयोग के साथ कुछ मुद्दों के बारे में सुना। –

0

ScopedEntityManager सहायक उपकरण मैं Github में बनाया है एक ऐसी ही तकनीक का उपयोग करता। अनुरोध फ़िल्टर के बजाय मैंने जीवन चक्र प्रबंधन के लिए ServletRequestListener चुना है। इसके अलावा मैं थ्रेडलोकल का उपयोग नहीं कर रहा हूं क्योंकि जे 2 ईई कंटेनरों में मेमोरी लीक की आदत है यदि सावधानी से प्रोग्राम नहीं किया गया है। टॉमकैट में असफल कुछ मानव त्रुटियों के लिए कुछ चाल हैं।