Java EE 5 の紹介

主要な機能拡張による、迅速かつ容易で単純化されたエンタープライズ・アプリケーション開発

新たな Java™ EE 5 仕様、その大幅な機能拡張の数々、そしてこの仕様が WebSphere® アプリショーンにとって何を意味するかを概説します。

Roland Barcia (barcia@us.ibm.com), Consulting IT Specialist, IBM

Roland BarciaRoland Barcia は、IBM Software Services for WebSphere の顧問 IT スペシャリストです。『IBM WebSphere: Deployment and Advanced Configuration』の共著者でもあります。


developerWorks マスター著者レベル 2

2007年 8月 (初版 2007年 7月 25日)

はじめに

Java EE (Java Platform, Enterprise Edition) 仕様の歴史を見てみると、主だった改訂のそれぞれが重要なテーマに沿っていたことがわかります。例えば、J2EE™ 1.2 の最初のリリースでは個々の仕様が初めて結合され、その後の J2EE 1.4 では Web サービスに重点が置かれています。図 1 は、Java EE の歴史を要約したもので、各バージョンの主要な特徴と、そのバージョンに外部から大きな影響を与えたものを記載してあります。

図 1. Java EE の歴史
Java EE の歴史

大抵の新しい技術の初期バージョンがそうであるように、Java EE 仕様の以前のバージョンでは以下をはじめとするさまざまな「苦労をするポイント」が明るみになりました。

  • ビジネス・ロジック・プログラミングの複雑さ
  • パーシスタンス・プログラミング・モデルの複雑さとパフォーマンス
  • プレゼンテーション層/ロジックの混同
  • Web サービスのタイプ、複雑さ、文書モデル、拡張、そしてパフォーマンス
  • 複数のメンバーによるチーム開発
  • 編集からコンパイル、デバッグに至るまでの長期間のサイクル

このような理由から、Java EE 5 仕様が単純化をテーマにしているのも当然のことです。この単純化という目的は、以下の領域における開発エクスペリエンスの改善によって達成されました。

  • 単純化されたビジネス・ロジック開発
  • 単純化されたテストおよび依存関係管理
  • 単純化された O/R パーシスタンス
  • 拡張された Web サービス・プログラミング・モデル

Java EE 5 におけるアップグレード内容の多くに影響を与えたのは、商用およびオープン・ソースの世界での革新的技術 (Hibernate、Spring、SDO (Service Data Object) など) です。さらに、仕様レベルで期待されるマイナーな機能拡張によってもアップグレードが実現されています。

EJB 3.0 では Hibernate、JDO (Java Data Objects)、TopLink などの商用およびオープン・ソースの製品や技術の影響を受け

Java EE 5 に取り組む準備として、この記事では EJB 3.0、JPA (Java Persistance Architecture)、Web サービス、JAX-WS などの新しい仕様の主要な特徴を取り上げ、最後に将来 Java EE 6 に期待できるであろう内容を簡単に紹介します。


EJB 3.0

Java EE 5 での技術強化のなかでとりわけ顕著なのは、開発を劇的に単純化するあからさまで大きな変更が行われた EJB (Enterprise JavaBean™) 3.0 です。EJB 3.0 仕様は現在、以下の 3 つの仕様に分割されています。

  • EJB 3.0 Simplified API: EJB コンポーネント (具体的には、セッション Bean とメッセージ駆動型 Bean) のコーディングに用いる新たな簡易 API を定義しています。

  • Core Contracts and Requirements: Bean と EJB コンテナーとの間の EJB 契約を定義しています。

  • Java Persistence Architecture API: パーシスタンスのための新しいエンティティー Bean モデルを定義しています。

以降のセクションでは、EJB 3.0 の更新と JPA (Java Persistence Architecture) API の更新について説明します。

EJB 3.0 の単純化

最近よく使われている POJO (Plain Old Java Object) という言葉は、単純な Java クラスで作成されたコードを意味します。EJB 2.x でのプログラムでは特定のクラスを継承し、複数のインターフェースを提供してデプロイメント記述子を作成しなければならないため、もはや単純とは言えない「詰め込みすぎの」Java オブジェクトとして捉えられています。これらのオブジェクトを実行してテストするには、J2EE コンテナーが必要です。この点が、EJB 3.0 では以下のように変更されています。

  • EJB コンポーネントにホーム・インターフェースが必要なくなりました。さらに、EJB コンポーネントがさまざまなインターフェースを提供したり、あるいは EJB 固有のクラスを継承する必要もありません。

  • J2SE 5.0 注釈が主体となって、EJB 3.0 コンポーネントを実装しやすくしています。開発者はXML の代わりに特殊な注釈を指定することで、EJB コンポーネントである POJO クラスを作成することができます。

  • EJB 3.0 では個別のリモート・インターフェースやローカル・インターフェースではなく、ビジネス・インターフェースの概念を導入しています。以下は、その一例です。

    リスト 1
    public interface Stock
    {
            public double getQuote(String symbol);
    }

    Bean クラスでは、このインターフェースを以下のように実装できます。

    リスト 2
    @Stateless public class StockBean implements Stock
            public double getQuote(String symbol)
            {
                    return 100.33;
            }
    }

    上記の例で @Stateless 注釈が意味するのは、このクラスは今やステートレス・セッション Bean であり、このクラスを呼び出すにはビジネス・インターフェースが使用されるということです。ステートレス・セッション Bean は特定のインターフェースを実装しなくてもコーディングすることが可能で、コンテナーが代わって生成してくれます。

    リスト. 3
    @Stateless public class StockBean
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    }

    これでインターフェースが 1 つできましたが、メソッドがリモート・メソッドであるか、またはローカル・メソッドであるかを指定するにはどうすればいいでしょうか。この場合ももちろん、注釈を使用できます。

    リスト. 4
    @Remote
    	public interface Stock
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    }

    注釈はビジネス・インターフェースまたは Bean クラス自体に付けることができますが、ビジネス・インターフェースを生成する場合には Bean クラスの注釈を有効にするほうが便利です。

EJB 3.0 仕様での更新には以下が含まれます。

  1. コンテナー・サービス
  2. コールバック
  3. インターセプター
  4. 依存性の注入
  1. コンテナー・サービス

    EJB コンポーネントがよく使われる理由は、トランザクション管理とセキュリティーを暗黙的にサポートしているからです。EJB 3.0 仕様ではコンテナー・サービスを適用するために (XML の他に) 注釈を使用します。以下は、トランザクション関連の属性をステートレス・セッション Bean に指定する例です。

    リスト. 5
    @Stateless public class StockBean
    {
    
    @TransactionAttribute(TransactionAttributeType.REQUIRESNEW)
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    }

    この注釈は、メソッドが新しいトランザクションで実行されるということを意味します。それぞれの注釈の構文とセマンティクスについては仕様を参照すればわかりますが、同じトランザクションおよびセキュリティー機能のすべてに注釈が付きます。コンテナー・サービスを適用するには XML デプロイメント記述子を使用するという方法もあります。その場合、注釈のオーバーライドも指定できるので、デプロイメント時に柔軟性がもたらされます。

  2. コールバック

    コールバックについてはどうかと言えば、EJB 3.0 より前のバージョンでは Bean クラスにコールバック・メソッド (ejbCreate() など) を実装しなければなりませんでした。つまり、Bean クラスはメソッドを使うかどうかには関わらず、すべてのメソッドを実装する必要があったということです。大抵の場合、これらのメソッドの実装は空でした。EJB 3.0 では、コールバック・メソッドまたはコールバック・リスナー・クラスのいずれかを使用して、注釈によってもコールバックを扱えるようになっています。以下は、コールバック・メソッドを使ってコールバックに応答するコードを作成する場合の例です。

    リスト. 6
    @Stateless public class StockBean implements Stock
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    
    	@PostConstruct initializeCache()
    	{
    	}
    }

    上記のコードによって、Bean インスタンスが作成された後にコードを実装できるようになります。一方、コールバック・リスナーを使用する場合は、以下のようにしてコールバック・リスナー・クラスを作成できます。

    リスト. 7
    public class MyCallbackListener
    {
    	@PrePassivate public clearCache(Object obj)
    	{
    		Stock stock = (Stock) obj;
    		//perform logic
    	}
    }

    Bean クラスに含まれないコールバック・クラスには、java.lang.Object パラメーターを含める必要があります。するとコンテナーは Bean インスタンスを渡すことになります。Bean クラスはコールバック・リスナー・クラスを追加するために、以下のように Bean クラス・レベルで特殊なコールバック注釈を使用します。

    リスト. 8
    @CallbackListener MyCallbackListener
    @Stateless public class StockBean implements Stock
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    }

    コールバックのいいところは、インターフェースを実装するときとは違って、コードにコールバックを組み込むかどうかは条件によるという点です。

  3. インターセプター

    嬉しいことに、EJB 仕様にはインターセプターが使用できるように追加されています。これまで EJB コンポーネントに欠けていたのは、サーブレット・フィルターがサーブレットに対して行うような、プリプロセッシングやポストプロセッシング、そして分野横断的な懸念に対処するアスペクト指向開発 (AOP) だったからです。今ではインターセプター・クラスを開発し、そのクラスを Bean に適用できるようになっています。以下の例は、 StockBean クラスの呼び出しを監査するインターセプターです。

    リスト. 9
    public class StockRequestAudit {
    	@AroundInvoke
    	public Object auditStockOperation(InvocationContext inv) throws
    	Exception {
    		try {
    			Object result = inv.proceed();
    			Auditor.audit(inv.getMethod().getName(), inv.getParameters[0]);
    			return result;
    		} catch (Exception ex) {
    			Auditor.auditFailure(ex);
    			throw ex;
    		}
    	}
    }

    上記のインターセプターはターゲット EJB メソッドの呼び出しをインターセプトして、InvocationContext の proceed() メソッドを呼び出します。これにより、実際に呼び出された EJB メソッドにまで呼び出しがフローできるようになります。ターゲット EJB メソッドからリターンした後、インターセプターは InvocationTarget 内のメタデータを使用して、呼び出されたターゲット EJB コンポーネントに含まれるメソッド名とパラメーターを取得します。その後は、以下のようにしてインターセプターを Bean クラスに適用することができます。

    リスト. 10
    @Stateless @Interceptors({StockRequestAudit})
    public class StockBean implements Stock
    	public double getQuote(String symbol)
    	{
    		return 100.33;
    	}
    }

    さらに、Bean クラス内に実装するインターセプター・メソッドを開発したり、複数のインターセプターを指定することも可能です。複数のインターセプターを指定する場合、インターセプターが呼び出される順序は Bean クラスに定義された順となります。インターセプターは Bean ではなく XML を使用して適用することも可能です。AOP では分野横断的な考慮事項を透過的に Bean に適用する必要があるため、XML を使用する方法のほうが適しています。

  4. 依存性注入

    EJB コードの依存関係 (データ・ソースなどのことに関する依存関係)、そして EJB クライアントによる EJB コンポーネントの呼び出し方法は、EJB開発テストを難しくしている要因の一部です。EJB 3.0 仕様では、このような厄介さを緩和する仕組みとして、依存性注入を導入しています。EJB が JNDI ルックアップを使用する代わりに、コードを注入することによってリソース参照を定義できるようにしているというわけです。以下は、JDBC 作業を行うために、別の EJB コンポーネントを呼び出してデータ・ソースを使用しなければならない EJB Bean の例です。

    リスト. 11
    @Stateless public class StockBean implements Stock
    {
    @EJB(name="MarketBean", businessInterface="Market")
    Market market;
    
    @Resource(name="StockDB, resourceType="javax.sql.DataSource")
    DataSource stockDS
    
    	public double getQuote(String symbol)
    	{
    		Connection con = stockDS.getConnection();
    		//DO JDBC work
    
    			return market.getCurrentPrice(symbol);
    	}
    }

    依存性注入は、setter メソッドやクラス変数など、さまざまな方法で行われます。詳細については、仕様を参照してください。

JPA (Java Persistence Architecture)

EJB パーシスタンスに関する仕様は大幅に変更されています。CMP (Container Managed Persistence) として知られるこの仕様はマッピング層を定義していないためコンテナーによって管理され、したがってマッピングの実装はベンダーに任されていました。EJB 3.0 では Hibernate、JDO (Java Data Objects)、TopLink などの商用およびオープン・ソースの製品や技術の影響を受け、新しいスタイルのパーシスタンスを導入しています。この POJO ベースのパーシスタンスには、以下の特徴があります。

  • 成功したパターンに基づいてモデル化されています。
  • JDBC アクセス・パターンを単純化します。
  • Web サービスとの統合が可能です。
  • コンテナーは関与しません。JPA は Java EE または Java SE 環境で使用できます。
  • O/R マッピング・メタデータが標準化されています。
  • トップダウン型、ミート・イン・ザ・ミドル型、ボトムアップ型の開発をサポートします。
  • 切断および接続オブジェクトの状態をサポートするため、データ転送オブジェクトを別途必要としません。図 2 はその一例です。
図 2. オブジェクト状態のサポート
オブジェクト状態のサポート

JPA API の更新には以下のものが含まれます。

  1. タイプ: エンティティーおよびテーブル
  2. インスタンス: Java オブジェクト
  3. 属性: Java プロパティーおよび Column 注釈
  4. 従属オブジェクト: 組み込み Java オブジェクト
  5. 派生属性: Transient 注釈
  6. キー属性: 注釈付きフィールドおよびキー・クラス
  7. 関係: 注釈と結合列
  8. 制約: 注釈およびデータベース
  9. 継承: 注釈 議論する 単一テーブル、結合テーブル、およびクラスごとのテーブル
  1. タイプ: エンティティーおよびテーブル

    JPA でのタイプであるエンティティー (エンティティー Bean とは呼ばれなくなりました) は、テーブルにマッピングできます。主なマッピング手段は、エンティティー・クラスに注釈を付けることです。以下の例では、Customer という Java オブジェクトと CUSTOMER テーブルとをマッピングしています。

    リスト. 12
    @Entity
    @Table(name="CUSTOMER")
    public class Customer implements Serializable {
    ...

    クラスにはエンティティーとして注釈が付けられ、Customer の注釈が付けられています。この代わりの手段、あるいはオーバーライド・メカニズムとしては、デプロイメント記述子を使用できます。

  2. インスタンス: Java オブジェクト

    アプリケーションは実行時に Java オブジェクトとやりとりをします。アプリケーションがオブジェクトに問い合わせをしたり、オブジェクトを存続させるために使用するのは、エンティティー・マネージャーという特殊なオブジェクトです。エンティティー・マネージャーは EJB コンテナー内の依存性注入を使用してアプリケーションに注入されます (または、Java SE 環境では EntityManagerFactory によってエンティティー・マネージャーを参照することもできます)。以下はその一例です。

    リスト. 13
    @PersistenceContext (unitName="db2")
    private EntityManager em;

    アプリケーションは、エンティティー・マネージャーからオブジェクトを取得したり、エンティティー・マネージャーにオブジェクトを渡すことができます。以下は、アプリケーションがエンティティー・マネージャーを使用して、オブジェクトを主キーで検索する例です。

    リスト. 14
    Customer customer = (Customer)em.find(Customer.class,customerId);

    もう 1 つの例として、Java オブジェクトを作成し、それをエンティティー・マネージャーに渡してデータベースに存続させる場合を以下に示します。

    リスト. 15
    CustomerOrder newOrder = new CustomerOrder();
    newOrder.setStatus("OPEN");
    newOrder.setTotal(new Integer(0));
    newOrder.setCustomerId(customerId);
    em.persist(newOrder);
  3. 属性: Java プロパティーおよび Column 注釈

    属性は、クラスに含まれる Java プロパティーです。Java プロパティーは @Column 注釈によってデータベース列にマッピングすることができます。マッピング・プロパティーには、フィールド (Field) とプロパティー (Property、デフォルト) という 2 つの形式があります。

    リスト. 16
    @Entity(access=FIELD)
    @Table(name="PRODUCT")
    public class Product implements Serializable {
    
    	@Id
    	@Column(name="SKU")
    	Integer sku;
    
    	@Column(name="DESC")
    	String description;
    
    	@Column(name="PRICE")
    	Integer cost;
  4. 従属オブジェクト: 組み込み Java オブジェクト

    JPA は従属オブジェクトをサポートします。クラスに @Embeddable の注釈を付けて定義する、組み込みオブジェクトと呼ばれる特殊なオブジェクトを作成することができます。

    リスト. 17
    @Embeddable
    public class CustomerAddress {
    private int streetAddress;
    private String city;
    private String state;
    private String country;
    ...
    }

    上記のオブジェクトをエンティティー・クラスにフィールドとして定義するには、以下のようにします。

    リスト. 18
    @Entity
    @Table(name="CUSTOMER")
    public class Customer {
    	private String name;
    	private CustomerAddress ca;
    
    @Embedded
    @AttributeOverrides({
    @AttributeOverride(name="streetAddress", column=@Column("
    STRT_ADD")),
    @AttributeOverride(name="city", column=@Column("CITY"))
    ... //more
    })
    	public CustomerAddress getCustomerAddress()
    {
    
    ...
    }

    上記のように特殊な属性オーバーライドを使用すれば、エンティティー内で組み込み可能なクラスのフィールドをマッピングすることができます。あるいは、組み込み可能なクラスで列を直接マッピングするという手段もあります。

  5. 派生属性: Transient 注釈

    デフォルトでは、すべてのフィールドは JPA で存続しますが、フィールドに一時的である (Transient) というマークを付け、ロジックを使用して任意のフィールドを派生させることも可能です。以下の例では、クエリーを実行してフィールドを派生させています。

    リスト. 19
    @Transient
    public Integer getTotal() {
    
    	Query totalLineItems = em.createNamedQuery("getOrderTotal");
    	totalLineItems.setParameter("orderId",orderId);
    	Integer result = (Integer)totalLineItems.getSingleResult();
    	return result;
    }
  6. キー属性: 注釈付きフィールドおよびキー・クラス

    JPA は主キーの複数のフレーバーをサポートするだけでなく、キー生成のさまざまなフレーバーもサポートします。単純な例としては、@Id 注釈を使用して、エンティティーのフィールドに主キーの注釈を付けることができます。

    リスト. 20
    @Entity
    @Table(name="CUSTOMER")
    public class Customer implements Serializable {
    
    	private Integer id;
    	private String name;
    	private CustomerOrder currentOrder;
    	
    		
    	@Id
    	@Column(name="CUST_ID")
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    ...

    また、複合キーを持つエンティティーに主キー・クラスを作成することもできます。

    リスト. 21
    public class LineItemId implements Serializable {
    
    	private Integer orderId;
    	private Integer productId;
    	public LineItemId() {
    		super();
    		// TODO Auto-generated constructor stub
    	}
    	@Column(name="ORDER_ID")
    	public Integer getOrderId() {
    		return orderId;
    	}
    	public void setOrderId(Integer orderId) {
    		this.orderId = orderId;
    	}
    	
    	@Column(name="PRODUCT_ID")
    	public Integer getProductId() {
    		return productId;
    	}
    	public void setProductId(Integer productId) {
    		this.productId = productId;
    	}
    	public boolean equals(Object arg0) {
    		if (arg0 == this) return true;
    		    if (!(arg0 instanceof LineItemId)) return false;
    			LineItemId other = (LineItemId)arg0;
    			if(other.getOrderId().equals(orderId) &&
    				other.getProductId().equals(productId))
    			{
    				return true;
    			}
    			return false;
    		
    	}
    	public int hashCode() {
    		return orderId + productId.hashCode();
    	}
    
    }

    さらに @IdClass 注釈を使って、エンティティーに複合キーを定義することができます。

    リスト. 22
    @Entity
    @Table(name="LINEITEM")
    @IdClass(LineItemId.class)
    public class LineItem implements Serializable {

    ここで必要となるのは、エンティティー・クラスに一致するフィールドがあることです。さもなければ、エンティティー・クラスがキーを組み込み可能なキーとして組み込むこともできます。

    リスト. 23
    @Entity
    @Table(name="LINEITEM")
    @IdClass(LineItemId.class)
    public class LineItem implements Serializable {
    	private LineItemId lineItemId;
    
    	@EmbeddedId
    public LineItemId getLineItemId()
    	{
    		return lineItemId;
    	}
    
    ...

    もう 1 つの主な機能拡張は、主キーを生成するためのサポートです。@Id 注釈の generate 属性を使うことで、異なる方法を選択することができます。例えば、以下のように主キーの生成を DB2 の IDENTITY 列に委任することが可能です。

    リスト. 24
    @Id(generate=GeneratorType.IDENTITY)
    @Column(name="ORDER_ID")
    public Integer getOrderId() {
    	return orderId;
    }
    public void setOrderId(Integer orderId) {
    	this.orderId = orderId;
    }

    サポートされるメカニズムには、他にもシーケンスとテーブルの生成があります。

  7. 関係: 注釈と結合列

    JPA にはエンティティー相互の関係に対する強力なサポートがあります。JPA がサポートする関係は、1 対 1、1 対多、多対 1、そして多対多です。JPA での関係は EJB 2.x のときのように双方向ではありません。オブジェクトは他のエンティティーをメンバーとして宣言し、その関係を定義する注釈が追加されます。特殊な属性を使えば、関係を双方向にすることもできます。以下は、2 つの異なる関係を持つ CustomerOrder クラスの例です。

    リスト. 25
    @Entity
    @Table(name="CUSTOMER")
    public class Customer implements Serializable {
    
    	
    	private Integer id;
    	private String name;
    	private CustomerOrder currentOrder;
    	
    		
    	@Id
    	@Column(name="CUST_ID")
    	public Integer getId() {
    		return id;
    	}
    	public void setId(Integer id) {
    		this.id = id;
    	}
    	
    	@Column(name="NAME")
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	
    	@OneToOne(cascade=CascadeType.ALL , fetch=FetchType.EAGER )
    	@JoinColumn(name="OPEN_ORDER_ID",referencedColumnName="ORDER_ID")
    	public CustomerOrder getCurrentOrder() {
    		return currentOrder;
    	}
    	public void setCurrentOrder(CustomerOrder currentOrder) {
    		this.currentOrder = currentOrder;
    	}
    
    }

    上記の関係では、Customer クラスの CustomerOrder プロパティーに注釈を付けることで、Customer に CustomerOrder との 1 対 1 の関係を定義しています。さらに JoinColumn 情報も定義しています。name 属性が Customer クラスのマッピング先とするテーブルの外部キーを定義し、参照先列が CustomerOrder テーブルのマッピング先とする主キーを定義します。関係に関する制約はすべて、属性として @OneToOne 注釈に定義されます。次に、以下に示す CustomerOrder クラスを見てください。CustomerOrder にも Customer プロパティーが定義されていますが、CustomerOrder オブジェクトに対する Customer 側からの関係は 1 対 多です。これは、Customer が持てる現行の注文は 1 つだけだからです。実際には、カスタマーは多くの注文を行っている場合があるため、CustomerOrder がそれを示しています。ただし、ここでは mappedBy 属性を使用して、関係の反対側においては CustomerOrder が Customer クラスによってマッピングされることを定義しています。このクラスを以下に示します。

    リスト. 26
    public class CustomerOrder implements Serializable {
    	private Integer orderId;
    	private String status;
    	private Integer total;
    	private Integer customerId;
    	private Collection<LineItem> lineItems;
    	private Customer customer;
    	
    	@Column(name="CUSTOMER_ID")
    	public Integer getCustomerId() {
    		return customerId;
    	}
    	public void setCustomerId(Integer customerId) {
    		this.customerId = customerId;
    	}
    	
    	@Id(generate=GeneratorType.IDENTITY)
    	@Column(name="ORDER_ID")
    	public Integer getOrderId() {
    		return orderId;
    	}
    	public void setOrderId(Integer orderId) {
    		this.orderId = orderId;
    	}
    	@Column(name="STATUS")
    	public String getStatus() {
    		return status;
    	}
    	public void setStatus(String status) {
    		this.status = status;
    	}
    	@Column(name="TOTAL")
    	public Integer getTotal() {
    		return total;
    	}
    	public void setTotal(Integer total) {
    		this.total = total;
    	}
    
    	@ManyToOne(fetch=FetchType.EAGER,optional=false)
    	  @JoinColumn(name="CUSTOMER_ID",
    	     referencedColumnName="CUST_ID",insertable=false,updatable=false,
    	     nullable=false,unique=true) 
    	  public Customer getCustomer() {
    	     return customer;
    	  }
    	  public void setCustomer(Customer customer) {
    	     this.customer = customer;
    	  }
    	  @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL ,
    	     fetch=FetchType.EAGER)
    	  public Collection<LineItem> getLineItems() {
    	     return lineItems;
    	  }
    	  public void setLineItems(Collection<LineItem> lineItems) {
    	     this.lineItems = lineItems;
    
    	}
    
    }

    CustomerOrder のもう 1 つの関係には、LineItem オブジェクトの集合もあります。これは 1 対多の関係です。JRE 5 では Generics を使用して集合のタイプを指定することを定義しています。ここでも特殊な mappedBy 属性を使用して、関係の反対側によってマッピングされる双方向関係にしています。

    リスト. 27
    @Entity
    @Table(name="LINEITEM")
    @IdClass(LineItemId.class)
    public class LineItem implements Serializable {
    	
    	private Product product;
    	private Integer orderId;
    	private Integer productId;
    	private Integer quantity;
    	private Integer total;
    	private CustomerOrder customerOrder;
    	
    	@Column(name="QUANTITY")
    	public Integer getQuantity() {
    		return quantity;
    	}
    	public void setQuantity(Integer quantity) {
    		this.quantity = quantity;
    	}
    	
    	@Column(name="AMOUNT")
    	public Integer getTotal() {
    		return total;
    	}
    	public void setTotal(Integer total) {
    		this.total = total;
    	}
    	
    	  @ManyToOne(fetch=FetchType.EAGER,optional=false)
    	  @JoinColumn(name="PRODUCT_ID",
    	     referencedColumnName="SKU",insertable=false,updatable=false,
    	     nullable=false,unique=true)
    	  public Product getProduct() {
    	     return product;
    	  }
    	  public void setProduct(Product product) {
    	     this.product = product;
    	  }
    	
    	@Column(name="ORDER_ID")
    	public Integer getOrderId() {
    		return orderId;
    	}
    	public void setOrderId(Integer orderId) {
    		this.orderId = orderId;
    	}
    	
    	@Column(name="PRODUCT_ID")
    	public Integer getProductId() {
    		return productId;
    	}
    	public void setProductId(Integer productId) {
    		this.productId = productId;
    	}
    	  @ManyToOne(fetch=FetchType.EAGER,optional=false)
    	  @JoinColumn(name="ORDER_ID",
    	     referencedColumnName="ORDER_ID",insertable=false,updatable=false,
    	     nullable=false,unique=true)
    	  public CustomerOrder getCustomerOrder() {
    
    	     return customerOrder;
    	  }
    	  public void setCustomerOrder(CustomerOrder customerOrder) {
    	     this.customerOrder = customerOrder;
    	  }
    
    }

    LineItem クラスには CustomerOrder プロパティーがあります。上記を見るとわかるように、ここには多対 1 の関係が定義されています。同様に、LineItem クラスには product オブジェクトに対しても多対 1 の関係があります。

    マッピングの別のタイプは、テーブルに 1 対 1 の関係が考えられる一方、オブジェクト・モデルには 1 つのオブジェクトしかないという場合です。つまり、単一のオブジェクトを複数のテーブルにマッピングしなければならない場合です (いわゆる従属オブジェクトの逆)。それを可能にするには、1 つ以上のセカンダリー・テーブルを追加します。以下は、Customer オブジェクトを Customer テーブルと Order テーブルにマッピングする例です。

    リスト. 28
    @Entity
    @Table(name="CUSTOMER")
    @SecondaryTable(name="ORDER ",
    pkJoin=@PrimaryKeyJoinColumn(name="CUST_ID"))
    public class Customer { ... }
  8. 制約: 注釈およびデータベース

    各種のデータベース制約をサポートする他、JPA ではさまざまな関係に制約を定義することもできます。

    リスト. 29
    @OneToMany(mappedBy="customerOrder", cascade=CascadeType.ALL , 
    fetch=FetchType.EAGER)
    	public Collection<LineItem> getLineItems() {
    		return lineItems;
    	}
    	public void setLineItems(Collection<LineItem> lineItems) {
    		this.lineItems = lineItems;
    	}

    上記の例で示しているのはカスケード効果です。例えば customer が削除された場合には、order も同じく削除されなければなりません。そこで、以下の例では制約を追加しています。

    リスト. 30
    @ManyToOne(fetch=FetchType.EAGER,optional=false)
    @JoinColumn(name="CUSTOMER_ID", referencedColumnName="CUST_ID",
    	insertable=false,updatable=false, nullable=false,unique=true) 
    
    public Customer getCustomer() {
    	return customer;
    }
    public void setCustomer(Customer customer) {
    	this.customer = customer;
    }

    上記では、主キーの一部でもある外部キーに多対 1 の関係を定義しています。この例では誰もこの列に対して更新または挿入することができません。また、この列は unique であり、NULL は可能ではないことも明確にされています。関係も同じく非オプションとして指定されます。以下に示すように、JPA では特定のエンティティーに固有の制約を定義することもできます。

    リスト. 31
    @Entity
    @Table(
    name="EMPLOYEE",
    uniqueConstraints=
    {@UniqueConstraint(columnNames={"EMP_ID", "EMP_NAME"})}
    )
  9. 継承: 注釈 議論する 単一テーブル、結合テーブル、およびクラスごとのテーブル

    JPA 仕様では、継承のマッピング用に以下の 3 種類のサポートを定義していますが、現行の仕様で必須となっているのはそのうちの 1 つだけです。

    • 単一テーブル: オブジェクト・ツリーは 1 つのテーブルにマッピングされます。
    • 結合テーブル: (オプション) 子クラスは、親がマッピングされているテーブルに対して外部キーの関係を持つテーブルにマッピングされます。
    • クラスごとのテーブル: (オプション) それぞれの具体的なサブクラスはテーブルにマッピングされ、スーパークラス属性の列を含みます。

    以下は、単一テーブルの方法を使用してスーパークラスとサブクラスをマッピングする例です。

    リスト. 32

    リスティングを見るにはここをクリック

    リスト. 32

    @Entity
    @Table(name="CUST")@Inheritance(strategy=SINGLE_TABLE,discriminatorType=STRING,discriminatorValue="CUST")
    public class Customer { ... }
    
    
    @Entity
    @Inheritance(discriminatorValue="VCUST")
    public class ValuedCustomer extends Customer { ... }

    以下は、結合テーブルの方法を使用した例です。

    リスト. 33
    @Entity
    @Table(name="CUST")@Inheritance(strategy=JOINED,discriminatorType=STRING,discriminatorValue="CUST")
    public class Customer { ... }
    
    @Entity
    @Table(name="VCUST")@Inheritance(discriminatorValue="VCUST")@PrimaryKeyJoinColumn(name="CUST_ID")
    public class ValuedCustomer extends Customer { ... }

    JPA に用意されているその他多くの機能を調べるには、「参考文献」を参照してください。


JAX-WS

Java EE 5 では、Web サービスの新しいプログラミング・モデル、JAX-WS も導入しています。このモデルの何が違うのかを説明する前に、JAX-RPC から JAX-WS への変更でそのまま変わっていない内容について説明しておきます。

  • JAX-WS では引き続き SOAP 1.1 over HTTP 1.1 をサポートしているので、相互運用性には影響がなく、同じメッセージを流すことができます。
  • JAX-WS では引き続き WSDL 1.1 をサポートしているため、この仕様に関して学んだことはそのまま有効です。WSDL 2.0 仕様は完成に近づいていますが、JAX-WS 2.0 が最終的に決定した時点では、まだ作業中のままでした。

JAX-WS については連載記事「Web サービスのヒント: JAX-RPC と JAX-WS」で詳しく説明しているので、ここでは JAX-RPC 1.1 から JAX-WS 2.0 への変更内容を要約します。

  • SOAP 1.2: JAX-RPC と JAX-WS はどちらも SOAP 1.1 をサポートしますが、JAX-WS は SOAP 1.2 もサポートします。
  • XML/HTTP: WSDL 1.1 仕様では、SOAP を使わずに HTTP で XML メッセージを送信する手段として HTTP バインディングを定義しました。JAX-RPC ではこの HTTP バインディングを無視しましたが、JAX-WS は HTTP バインディングをサポートします。
  • WS-I Basic Profiles: JAX-RPC はWS-I の BP (Basic Profile) バージョン 1.0 をサポートします。一方、JAX-WS がサポートするのは BP 1.1 です (WS-I とは、Web サービスの相互運用性のための組織です)。
  • 新しい Java 機能: JAX-RPC は Java 1.4 にマッピングされる一方、JAX-WS は Java 5.0 にマッピングされます。JAX-WS は、Java 5.0 の新機能の多くに依存しています (J2EE 1.4 の後継である Java EE 5 では JAX-WS のサポートが追加されていますが、JAX-RPC も引き続きサポートされています。これは今日の Web サービス入門者にとっては、ややこしく思えるかもしれません)。
  • データ・マッピング・モデル: JAX-RPC が持つ独自のデータ・マッピング・モデルは、すべてのスキーマ型の約 90% をカバーしています。このマッピング・モデルでカバーされないものは、javax.xml.soap.SOAPElement にマッピングされます (JAX-WS データ・マッピング・モデルは JAXB であり、JAXB はすべての XML スキーマへのマッピングを保証しています)。
  • インターフェース・マッピング・モデル: JAX-WS の基本的なインターフェース・マッピング・モデルには JAX-RPC のモデルとの大きな違いはありませんが、JAX-WS のモデルは Java 5.0 の新しい機能を利用し、非同期機能を導入しています。
  • 動的プログラミング・モデル: JAX-WS の動的クライアント・モデルは JAX-RPC のモデルとは大きく異なります。変更の多くは、メッセージ指向の機能や動的な非同期機能をはじめ、業界からの要望を認識したことによるものです。また、JAX-WS は JAX-RPC にはない動的サーバー・モデルも追加しています。
  • MTOM (Message Transmission Optimization Mechanism): JAX-WS は JAXB を通して、添付の相互運用性を実現する新しい添付仕様、MTOM のサポートを追加しています。
  • ハンドラー・モデル: ハンドラー・モデルは JAX-RPC と JAX-WS との間で大幅に変更されました。JAX-RPC ハンドラーが SAAJ 1.2 に依存するのに対し、JAX-WS ハンドラーは新しい SAAJ 1.3 仕様に依存しています。

JAX-WS はプログラミング・モデルを単純化するために EJB 3.0 とも連動します。例えば以下のコードを見ると、EJB 3.0 POJO を Web サービスにするのがいかに簡単なのかがわかります。

リスト. 34
@WebService public interface StockQuote {
    public float getQuote(String sym);
}

@Stateless public class QuoteBean implements StockQuote {
	public float getQuote(String sym) { ... }
}

Web サービスでの一層高度な機能をサポートするための注釈も追加されています。JAX-B は、POJO (リスト 36) と XML スキーマ (リスト 37) との標準マッピングを行います。以下はその一例です。

リスト. 35
@XmlType
public class Trade {
	@XmlElement(name="tickerSymbol")
	public String symbol;
	@XmlAttribute
	int getQuantity() {...}
	void setQuantity() {...}
}
リスト. 36
<xs:complexType name="trade">
	<xs:sequence>
	<xs:element 
		name="tickerSymbol"
		type="xs:string"/>
	</xs:sequence>
	<xs:attribute name="quantity"
		type="xs:int"/>
</xs:complexType>

JavaServer Faces

JSF (JavaServer™ Faces) が登場してから数年が経ちますが、JSF は IBM® WebSphere Application Server をはじめ、ほとんどの Java EE アプリケーション・サーバーにサポートされています。現在、JSF は Java EE 5 仕様の一部となっています。以下のように、JSF が Java EE アプリケーションにもたらす利点は数多くあります。

  • 豊富で拡張可能な UI コンポーネント
  • イベントによる駆動
  • 管理されたコンポーネント状態
  • レンダラーとクライアントの独立性
  • バリデーター
  • 型変換
  • 外部化ナビゲーション
  • 共通 JavaServer Pages および Faces Expression Language

Java EE 6 への期待

最近、JSR 316 (Java EE 6) に関する提案が提出されました。仕様の定義においてはまだ初期段階ですが、この提案はいくつかの重要なテーマを浮き彫りにしています。

  • 拡張性: 拡張性ポイントとサービス・プロバイダー・インターフェースをさらに追加することにより、他の技術が手際よく効率的にプラットフォーム実装に適用できるようにして、拡張を可能にします。
  • プロファイル: JCP プロセスでの定義によると、プロファイルは Java EE プラットフォームを参照する予定で、Java EE platform 技術の一部または基本の Java EE プラットフォームには含まれない追加の JCP 技術、あるいはその両方を組み込む可能性があります。また、専門家グループは Web アプリケーション開発を対象とした Java EE プラットフォームの一部として、Java EE Web プロファイルの最初のバージョンも定義することになっています。
  • プルーニング: Java EE プラットフォームに含まれる一部の技術は、プラットフォームに導入された当時に比べ、関連性が低くなっています。そのため、これらの技術をプラットフォームから慎重かつ整然とした方法で「削除」し、該当する技術をまだ使用している開発者への影響を最小限に抑えると同時に、プラットフォームをさらに強化できるようにするための手段が必要です。当該プロセスで定義されているように、専門家グループは、将来の Java EE プラットフォーム仕様から削除した方がよい技術を検討することになっています。削除の対象としては、以下の仕様が考えられます。

    • EJB CMP (事実上、Java Persistence に置き換えられています。)
    • JAX-RPC (事実上、JAX-WS に置き換えられています。)
  • SOA サポート: Java EE プラットフォームはすでに、SOA アプリケーションで広範に使用されています。SOA アーキテクチャーのメリットを認識する企業が次第に増えるなか、プラットフォームに関する要件も機能性と相互運用性という両方の点で増大してきています。そのため、Java EE 6 では Web サービス・サポートの追加を検討する必要が出てくるはずです。基本的な Web サービス・サポートは Java SE 6 プラットフォームの一部となっていますが、この仕様ではさらにバージョンを新しくした技術によって、追加の Web サービス・サポートを提供しなければなりません。SOA 環境での複合アプリケーションで使用できそうな機能を定義しているのは、SCA (Service Component Architecture) です。専門家グループは現在、SCA が定義する機能を Java EE 6 プラットフォームに組み込むことが妥当であるかどうかを検討しているところです。

    • その他の追加: 専門家グループは、Java EE 6 に以下の新しい JSR を組み込むことを提案しました。
    • JSR-196 Java Authentication SPI for Containers
    • JSR-236 Timer for Application Servers
    • JSR-237 Work Manager for Application Servers
    • JSR-299 Web Beans
    • JSR-311 JAX-RS: Java API for RESTful Web Services
  • 以下の分野には、さらなる更新が期待されます。
  • Enterprise JavaBeans
  • Java Persistence API
  • Servlets
  • JavaServer Faces
  • JAX-WS
  • Java EE Connector API

組み込まれることになる一連の技術は、パートナーや顧客の要件に応じて専門家グループが決定します。これらの仕様の一部は、急速に台頭してきている Web 2.0 の世界にも目を向けることになるでしょう。


まとめ

Java EE 5 は、Java EE をエンタープライズ開発に最適なプラットフォームにする強力で重要なリリースです。明らかに、これまでの Java 開発に関連した批判の大部分に対処するために大々的な措置が取られています。EJB 3.0 と JPA は有力でしかも使いやすい技術です。さらに、JAX-WS での改善が、Web サービス開発を今までになく容易にしてくれます。


謝辞

この記事に貢献してくれた Jim Knutson 氏と Russell Butek 氏に感謝します。

参考文献

学ぶために

製品や技術を入手するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=WebSphere, Java technology, SOA and web services
ArticleID=278172
ArticleTitle=Java EE 5 の紹介
publish-date=08252007