※ EntityManagerのDIはできますが、データの更新が動きません。原因について調査中。取得と登録は問題ありません。
はじめに
JavaEEをやっていると、EntityManagerを扱うEJB(いわゆるビジネスロジッククラス)は通常ステートレス管理にします。理由としては、EntityManagerがスレッドセーフではないため、トランザクション単位でDIする必要があるためです。Springでいうところの@Service
、@Repository
は基本的にシングルトン管理になります。
じゃあ、JavaEEでもシングルトンにしましょう。
なぜシングルトンか
単刀直入に言うと、メモリ節約のためです。ステートレス管理の場合、EJBプールというメモリ領域にEJBのインスタンスが保持されます。必要なときにプールからインスタンスを取得しDIされます。domain.xmlなどのサーバ設定ファイルにEJBのプールサイズなどを設定できますが、これより多いリクエストが発生した際に、新たにインスタンスが作られることになります。インスタンスが生成されるということはそれだけメモリを使うことになります。一方、シングルトンの場合は、アプリケーションで1つしかインスタンスを持ちませんので、メモリの使用を抑えることができます。
NGパターン
@Singleton
public class UserService {
@PersistenceContext
private EntityManager em;
// 〜以下省略〜
}
スレッドセーフではないEntityManagerをアプリケーションで1つしか持たないようになっています。
OKパターン
EntityManagerを生成する EntityManagerFactory
というクラスが存在します。
このクラスはスレッドセーフであるため、シングルトン管理で問題ないのでこれを利用します。
@ApplicationScoped
public class EntityManagerFactoryService {
private static EntityManagerFactory factory;
public EntityManagerFactory getEntityManagerFactory() {
if (factory == null) {
factory = Persistence.createEntityManagerFactory("testUnit");
}
return factory;
}
@Produces
@RequestScoped
public EntityManager getEntityManager() {
return getEntityManagerFactory().createEntityManager();
}
@PreDestroy
public void destroy() {
if (factory.isOpen()) {
factory.close(); // クローズしないと再デプロイ時にエンティティが見つからなくなる。
}
}
}
@Singleton
public class UserService {
@Inject
private EntityManager em;
}