タイトル: EJB
SEOタイトル: EJB完全ガイド(Stateless/Stateful/Singleton/MDB/CDIとの違い/Spring代替)
| この記事の要点 |
|
EJB とは
EJB (Enterprise JavaBeans) は、Java EE(現 Jakarta EE)におけるサーバサイドの再利用可能コンポーネント仕様です。WebLogic / WildFly / GlassFish / Open Liberty などのEJB コンテナにデプロイされ、トランザクション・セキュリティ・並行制御・プーリングといった非機能要件をコンテナが面倒を見ます。
歴史と現状
| 世代 | 年 | 特徴 |
|---|---|---|
| EJB 1.x / 2.x | 1999-2003 | 重厚な XML 記述 / Home/Remote 二重インターフェース / 開発生産性悪い → 不人気 |
| EJB 3.0 | 2006 | アノテーション化、POJO ベース。Entity Bean は JPA として分離 |
| EJB 3.1 | 2009 | Singleton SessionBean、no-interface view、組み込み EJB Container |
| EJB 3.2 | 2013 | Java EE 7、徐々に CDI へシフト |
| Jakarta EE 8/9/10 | 2019- | 名前空間が javax.ejb → jakarta.ejb、EJB Lite で軽量化 |
SessionBean の種類
Stateless Session Bean
状態を持たない・最もよく使われる業務ロジック実装。コンテナがプールでインスタンスを使い回します。
import jakarta.ejb.Stateless;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
@Stateless
public class OrderService {
@PersistenceContext
private EntityManager em;
public Order createOrder(Long userId, List productIds) {
// メソッド単位で自動的にトランザクション開始/コミット
Order order = new Order(userId);
for (Long pid : productIds) {
Product p = em.find(Product.class, pid);
order.addItem(p);
}
em.persist(order);
return order;
}
}
// 呼び出し側
@Inject
private OrderService orderService;
orderService.createOrder(userId, productIds);
Stateful Session Bean
クライアントセッション単位で状態を保持する。ショッピングカートのような用途。
import jakarta.ejb.Stateful;
import jakarta.ejb.Remove;
@Stateful
public class ShoppingCart {
private final List- items = new ArrayList<>();
public void addItem(Item item) {
items.add(item);
}
public List
- getItems() {
return List.copyOf(items);
}
@Remove // 呼ばれるとインスタンス破棄
public void checkout() {
// ...
}
}
Singleton Session Bean
アプリ全体で1 インスタンス。設定キャッシュなどに。
import jakarta.ejb.Singleton;
import jakarta.ejb.Startup;
import jakarta.annotation.PostConstruct;
@Singleton
@Startup // 起動時に初期化
public class ConfigCache {
private Map cache = new ConcurrentHashMap<>();
@PostConstruct
public void load() {
// DB から設定読み込み
}
public String get(String key) {
return cache.get(key);
}
}
Message-Driven Bean (MDB)
JMS キュー/トピックを受信して非同期処理。
import jakarta.ejb.MessageDriven;
import jakarta.ejb.ActivationConfigProperty;
import jakarta.jms.Message;
import jakarta.jms.MessageListener;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "jakarta.jms.Queue"),
@ActivationConfigProperty(propertyName = "destination",
propertyValue = "java:/queue/orders")
})
public class OrderProcessor implements MessageListener {
@Override
public void onMessage(Message msg) {
// メッセージ処理
}
}
トランザクション制御
EJB はメソッド単位の宣言的トランザクションを持つのが最大の特徴:
import jakarta.ejb.TransactionAttribute;
import jakarta.ejb.TransactionAttributeType;
@Stateless
public class PaymentService {
// デフォルトで REQUIRED(既存 TX に参加、無ければ新規)
public void pay(Order o) { ... }
// 必ず新トランザクションで(既存 TX を中断)
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void logAuditTrail(Order o) { ... }
// トランザクション無しで実行
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public List readReport() { ... }
}
EJB と JPA Entity
かつての EJB 1.x/2.x では Entity Bean も EJB 仕様の一部でしたが、EJB 3.0 以降は JPA (Java Persistence API) として完全分離。Entity は単なる POJO + アノテーションです。
@Entity
@Table(name = "orders")
public class Order {
@Id @GeneratedValue
private Long id;
@ManyToOne
@JoinColumn(name = "user_id")
private User user;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List items;
// getter/setter
}
EJB vs CDI
| EJB | CDI | |
|---|---|---|
| 用途 | 業務ロジック・TX・分散 | DI とスコープ管理 |
| トランザクション | 宣言的 (デフォルト REQUIRED) | CDI 単体は無し、@Transactional で対応 |
| プーリング | あり (Stateless) | 無し(Scope に依存) |
| 分散呼び出し | RMI/IIOP, EJB Remote 可 | 不可 |
| 非同期 | @Asynchronous | CDI Async Events (4.0+) |
| セキュリティ | @RolesAllowed | Jakarta Security と統合 |
| 軽量性 | 重め | 軽い |
現代の Jakarta EE はCDI を主軸に、必要に応じて EJB の機能を借りる構成が主流。
Spring Boot との比較
| 機能 | EJB | Spring Boot |
|---|---|---|
| DI | @EJB / @Inject | @Autowired |
| 業務ロジック | @Stateless | @Service |
| トランザクション | 暗黙の REQUIRED | @Transactional 明示 |
| 非同期 | @Asynchronous | @Async + ThreadPool |
| メッセージング | MDB | @KafkaListener / @RabbitListener |
| 運用 | EJB コンテナ必須 (WildFly等) | 組込 Tomcat / Jetty で単独 JAR |
| 学習コスト | 高い | 低〜中 |
現代における EJB の立ち位置
- 新規プロジェクトの多くは Spring Boot または Micronaut / Quarkus
- Jakarta EE 新規案件でも EJB Lite(Stateless / トランザクション のみ)に絞る
- WebLogic / WAS など商用 EJB コンテナ運用中の既存システムでは現役
- 軽量化トレンド: マイクロサービス時代、起動の重い EJB は敬遠される傾向
- 金融・大企業の分散トランザクション (JTA + XA) で残る価値あり
FAQ
Q: EJB はもう使わなくていい?
A: 新規 Greenfield なら Spring Boot で十分です。ただし既存 EJB システムの保守、JTA 分散トランザクション要件、商用 AP サーバ標準化された企業では現役。
Q: @Stateless と Spring の @Service の違いは?
A: @Service は単なる DI 対象。@Stateless はプール管理 + 暗黙トランザクション + リモート呼び出し可。Spring で同等は @Service @Transactional。
Q: Stateful Session Bean は使うべき?
A: スケーラビリティ上、分散環境では扱いにくい(クライアント-Bean アフィニティ管理)。マイクロサービスでは Redis セッションや JWT を推奨。