2013-02-25 22 views
10

Struts 1.3 + JPA(永続化プロバイダとしてのHibernateを使用)を使用して、簡単な "Book Store"プロジェクトを開発しています。私はSpringや他のより洗練された開発環境(Jbossなど)に切り替えることができず、Hibernate特有の技術(例えば、Sessionクラス)を使用することはできません。JSEでJPAを使用したEntityManager ThreadLocalパターン

私がJSE環境にいるという事実を考えると、EntityManagerのライフサイクル全体を明示的に管理する必要があります。

次のようにBookエンティティが定義されています

@Entity 
public class Book { 

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

    // Getters and Setters 
} 

私は、すべての書籍のインスタンスを取得し、そのISBNによって単一本のインスタンスを取得し、取り外した本をマージするの、それぞれ担当している3つのActionクラスを、定義されましたDBに格納する。

ビジネスロジックコードとデータアクセスコードの間の懸念の分離を高めるために、私はCRUD操作を実行するための単なる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はスレッドセーフではないことを考えると、私はThreadLocal変数内EntityManagerをカプセル化しBookUnitSessionという名前のヘルパークラスを導入しました:

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クラスでした。したがって、各DAOオブジェクトは作業の必要な単位をカプセル化を担当する各要求は、それ自身のEntityManagerインスタンスを開閉する必要があり、セッションあたりの要求パターンによれば、EntityManagerHelper.getEntityManager()

を呼び出すことによって、正しいEntityManagerインスタンスを取得することができますトランザクション内。これは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ページは)彼らは怠惰に初期化されている場合でも、エンティティのフィールドを取得することができます(オープンセッションがでビューパターン)。 JSE環境では、サーブレットコンテナをシャットダウンするときにEntityManagerFactoryを明示的に閉じる必要があります。これはServletContextListenerオブジェクトを使用して行うことができます。

public class EntityManagerFactoryListener implements ServletContextListener { 

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

    @Override 
    public void contextInitialized(ServletContextEvent e) {} 

} 

web.xml配備記述子:私はGitHubの中で作成した

<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

いい仕事ですが、おそらくJSEではなくJEEを意味します。 –

+1

ありがとうございます。私は現在、アプリケーションサーバーなしでTomcat 7とStruts 1.3を使用しています。したがって、私は完全にJEEに準拠しているとは言えません。永続性の観点からは、JSE環境にいるようなものです。だからこそ、私はJSEをタイトルに挙げました。 –

+0

こんにちは、このソリューションは最後にうまくいったのですか? ThreadLocalの使用に関するいくつかの問題について聞いたことがあります。 –

0

ScopedEntityManagerヘルパーツールは、同様の手法を使用しています。リクエストフィルタの代わりに、私はライフサイクル管理のためにServletRequestListenerを選択しました。また、慎重にプログラムされていないと、J2EEコンテナでメモリリークの癖があるため、スレッドローカルを使用していません。 Tomcatには、人間の間違いをフェイルセーフするためのいくつかのトリックがあります。

関連する問題