http://www.oracle.com/technetwork/articles/java/springtojavaee-522240.html
はじめに
「Spring Frameworkは、Java Enterprise Edition(Java EE)よりもずっと使いやすい」と、Spring Frameworkの支持者は主張します。私はJava EEのファンですし、Java EEに関する本をいくつか執筆していますが、いつも好きなテクノロジスタックを選択できるわけではなく、Springを使うプロジェクトに参画しなければならなかったのは多くの開発者と同様です。
Springを使ったプロジェクトに参画するたびに、私は小声でぶつくさ言い始めるわけです。長く複雑なXMLファイルを読んで、このプロジェクトで何をしているのかを見つけ出さなければならない、ってことがわかっているのです。自分のプロジェクトには約一万もの依存関係があり、生成されたWARファイルが怪物になるってこともわかっているのです。
Java EEの場合、必要なサービスのほとんどは、アプリケーション·サーバーが提供してくれるので、必要な依存関係の数は最小限に抑えることができます。ほとんどのケースでは、Java EEは、必要な場合のみデフォルト設定を上書きする手法を提供します。つまり、ほんのちょっとだけ設定するだけで、大抵の場合は、デフォルト値を使用します。設定が必要な場合、通常はアノテーションで設定するわけですが、これによりソースコードを見るだけで全体像を把握することができるのです。XML構成ファイルとソースコードを行き来する必要はないのです。
Java EEプロジェクトであれば、前の段落で述べたすべての利点に加え、NetBeansで利用できる高度なツールを活用することができます。幸運にもGlassFish Server Open Source EditionもしくはOracle GlassGish Serverをアプリケーションサーバとして利用できるなら、「Deploy on Save」機能が使えます。この機能は、ファイルをプロジェクトに保存する都度、更新版を自動的にGlassFish Serverにバックグラウンドでデプロイするというものです。ブラウザ上のページをリロードすれば、変更が直ちに反映されています。これでかなり時間を節約できます。編集して、保存して、デプロイして、更新するというサイクルに戻ることを強制されるたびに、後ろ手に縛られて片手で作業するような感じに思うのです。
この一連の投稿では、Springで作成されているペットクリニックのサンプルアプリケーションをJava EEで書き換えます。第1部では、NetBeansが提供する優れたJava EEのツールを活用して、Springバージョンと同等の機能を持つアプリケーションを迅速に開発する方法をご説明します。 Java EE版ではユーザーインターフェイスにJavaServer Faces(JSF)を採用し、データアクセスオブジェクト(DAO)は、Enterprise JavaBeans(EJB)3.1のセッションBeanを使用して実装、データアクセスは、Java Persistence API(JPA)2.0を使っています。
第1部では、既存のデータベースから永続化層を生成して、アプリケーションのJava EE版の開発をはじめます。第2部では、NetBeansを使うとDAOとして振る舞うEJB 3.1session beanやJSF 2.0のユーザー·インタフェースを容易に作成できることをご説明します。
プロジェクトのセットアップ
MySQLがローカルマシン上にインストールされており、petclinicのデータベースが既にあるものとします(Pet Clinicに含まれるANTスクリプトのsetupDBで簡単に作成できます)。
まず、新しいWebプロジェクトを作成します(図1)。
図1:新しいプロジェクトを作成 |
図2:新しいプロジェクトの名前と場所を指定 |
図3:フレームワークとしてJavaServer Facesを選択 |
図4:サーバとJava EEのバージョンを選択 |
図5:新たに作成したプロジェクト |
では開発に入りましょう。
プロジェクトの開発
NetBeansはJava EEアプリケーションの開発に必要なほとんどのコード(JPAエンティティ、DAO、JSFページ、JSF managed beanなど)を生成してくれます。
まず、JPAエンティティを作成しましょう。ほとんどのJPA実装にはJPAエンティティからデータベースの表を自動生成する機能がありますが、逆はそうではありません。つまり、JPAにはJPAエンティティを既存のデータベース表から生成する機能が備わっていないのです。
このため、既存スキーマで作業をする場合(大抵の場合ですが)、JPAエンティティを手で作成し、適切なアノテーション、プロパティ、getter/setterなどを付加する必要があります。しかし、NetBeansでは、既存スキーマからJPAエンティティを自動生成する機能が備わっています。単に[ファイル]>[新規ファイル]から、[持続性]を選択し、[データベースからのエンティティクラス]を選択すればよいのです(図6)。
図6:データベースからのエンティティークラスを選択 |
図7:データソースの作成 |
図8:データベース接続の選択 |
図9:ドライバの選択 |
図10:詳細情報を指定し、接続をテスト |
図11:スキーマの選択 |
図12:エンティティクラスの指定 |
[訳注]原文では、vet_specialties表も選択されていますが、最新のNetBeansでは選択できませんでした。
図13:データベース表 |
NetBeansはデータベース表の名前を調べて、エンティティクラスの希望の名前を推測しようとします。PetClinicデータベースは表の名前に複数形の名前を使っています(例えば、owners、pets、specialitiesなど)。エンティティ名を単数形名詞(Owner、Pet、Specialityなど)にしたい場合、NetBeansでは便利なことに、このタイミングで、提示されるJPAエンティティクラス名をダブルクリックするだけで、必要に応じて修正することができます。
ここで、JPAエンティティの各フィールド用に名前付きクエリを生成したり、JAXB(Java API for XML Binding)アノテーションを作成したり、persistence unitを作成することができます。ほとんどの場合、名前付きクエリを生成し、persistence unitを作成するべきでしょう。JAXBアノテーションは不要かもしれませんが、特に害はないのでこの例ではJAXBアノテーションも生成することにしました。
[次へ]をクリックすると、マッピングオプションを指定できます(図14)。
[関連の取得]リストで、関連エンティティをどのようにロードするかを選択できます。デフォルトの挙動では1対1と多対1の関係のフェッチタイプはEAGER、1対多や多対多の関係のフェッチタイプはLAZYです。デフォルトの挙動を選択することができますし、全ての関係でフェッチタイプをEAGERもしくはLAZYと指定することができますが、大抵の場合はデフォルトの設定で問題ないでしょう。
図14:関連エンティティのロード方法を指定 |
[表を再生成するための属性」のチェックボックスを選択すると、生成されたJPAエンティティの@Column(時には@Table)アノテーションにさらに属性が追加されます。このチェックボックスを選択すると、メタデータをデータベースから取得し、そのメタデータでJPAアノテーションに追加する属性を付加します。
データベースがマッピング列のNULLを許容していない場合、nullable属性(falseが指定されています)を対応する@Columnアノテーションに追加します。String型の属性であれば、length属性を@Columnアノテーションに追加します。この属性で対応するプロパティが許容する最大文字列長を指定します。decimal型であれば、精度(総桁数)やスケール(小数点以下の桁数)を@Columnアノテーションに追加します。一意制約がある場合は、uniqueConstraints属性を@Tableアノテーションに追加します。
[関係に列名を使用]のチェックボックスを選択すると、リレーションで生成されたフィールド名をリレーションの「1」の部分のカラム名にちなんで命名します。例えば、CUSTOMERというデータベース表があり、ORDERというデータベース表と1対多のリレーションがあり、CUSTOMER表の列ORDER_IDがORDER表の主キーを指している場合、生成されるJPAエンティティのフィールド名はorderidになります。このチェックボックスを外すと、生成されたフィールド名はorderになります。経験上、チェックボックスを外したほうが妥当な名前になります。
[完了]をクリックした後、プロジェクトで生成されたJPAエンティティを確認できます(図15)。
図15:生成されたJPAエンティティ |
では先に進む前に理解を確認するため、生成されたエンティティの一つをみてみましょう。
コード1. 生成されたエンティティを調べてみるコード1のJPAエンティティの実際のコードはかなり平凡(敢えて言えばつまらない)です。privateプロパティやpublicのgetter/setterを持つ、標準的なJavaBeansでしかありません。興味深いところはアノテーションです。
package com.ensode.petclinicjavaee.entity; //imports omitted for brevity @Entity @Table(name = "owners", catalog = "petclinic", schema = "") @XmlRootElement @NamedQueries({ @NamedQuery(name = "Owner.findAll", query = "SELECT o FROM Owner o"), @NamedQuery(name = "Owner.findById", query = "SELECT o FROM Owner o WHERE o.id = :id"), @NamedQuery(name = "Owner.findByFirstName", query = "SELECT o FROM Owner o WHERE o.firstName = :firstName"), @NamedQuery(name = "Owner.findByLastName", query = "SELECT o FROM Owner o WHERE o.lastName = :lastName"), @NamedQuery(name = "Owner.findByAddress", query = "SELECT o FROM Owner o WHERE o.address = :address"), @NamedQuery(name = "Owner.findByCity", query = "SELECT o FROM Owner o WHERE o.city = :city"), @NamedQuery(name = "Owner.findByTelephone", query = "SELECT o FROM Owner o WHERE o.telephone = :telephone")}) public class Owner implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Basic(optional = false) @NotNull @Column(name = "id", nullable = false) private Integer id; @Size(max = 30) @Column(name = "first_name", length = 30) private String firstName; @Size(max = 30) @Column(name = "last_name", length = 30) private String lastName; @Size(max = 255) @Column(name = "address", length = 255) private String address; @Size(max = 80) @Column(name = "city", length = 80) private String city; @Size(max = 20) @Column(name = "telephone", length = 20) private String telephone; @OneToMany(cascade = CascadeType.ALL, mappedBy = "owner") private CollectionpetCollection; public Owner() { } public Owner(Integer id) { this.id = id; } //getters and setters omitted for brevity @Override public int hashCode() { int hash = 0; hash += (id != null ? id.hashCode() : 0); return hash; } @Override public boolean equals(Object object) { // TODO: Warning - this method won't work in the case the id // fields are not set if (!(object instanceof Owner)) { return false; } Owner other = (Owner) object; if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) { return false; } return true; } @Override public String toString() { return "com.ensode.petclinicjavaee.entity.Owner[ id=" + id + " ]"; } }
一目瞭然なのは、クラスが@Entityアノテーションが施されています。これが全てのJPAエンティティで必要だからです。
次に、@Tableアノテーションがあります。このアノテーションを使うほとんど共通する理由はJPAエンティティを対応するデータベース表にアノテーション名の属性を使ってマッピングするためです。これはJPAエンティティの名前がデータベース表名に一致しない場合にのみ必要です(この例の場合では)。
この特定の場合では、@Tableアノテーションはカタログ属性セットやスキーマ属性セットも持っていますが、これは[データベース表の完全修飾名]のチェックボックスをウィザードで選択したからです。MySQLはスキーマとデータベースを区別しないので、スキーマ属性に対応する生成値は空です。何を「カタログ」とするのかは、データベースベンダーによって異なります。MySQLの場合、カタログは単にデータベース名ですので、カタログの属性の対応する値を見てください。
次に、@XmlRootElementアノテーションですが、JAXBがエンティティをXMLにマッピングするために使います。これは、ウィザードで[JAXB注釈の生成]のチェックボックスをONにしたために追加されたものです。今回のサンプルではこの機能を使わない予定ですが、ただで付いてきたものなので、無害です。
生成された@NamedQueriesアノテーションは全ての生成された@NamedQueryアノテーションをカプセル化します。NetBeansウィザードは@NamedQueryアノテーションをエンティティの各フィールドに付けます。JPA名前付きクエリーを使うと、Java Persistence Query Language (JPQL)のクエリを対応するJPAエンティティで定義できます。これはコードの他の場所でクエリをハードコーディングしなくてよいということです。
@NamedQueryアノテーションで定義されるJPQLクエリには、JPA EntityManagerのcreateNamedQuery()メソッドを使ってアクセスできます。コロン(:)の後に続く識別子をパラメータがパラメータです。これらのパラメータはクエリ実行の前に適切な値に変更される必要がありますが、これはQueryオブジェクトのsetParameter()メソッドを呼び出して実施します。
@Idアノテーションを使って、エンティティのidプロパティが主キーであることを指定します。NetBeansのWizardは対応するデータベース表の主キーを自動的にインクリメントし、適切なJPA主キー生成strategyを使います。これは@GeneratedValueアノテーションで実現します。
@Idフィールドに対する@Columnアノテーションで、NULLを許容する属性にfalseを指定しています。対応するデータベース中の列がNULLを許容していないことをNetBeansのウィザードが検知したため、自動的にアノテーションにこの属性を追加しました。同様に、String型の全てのフィールドに対する@Columnアノテーションはすべてlength属性を持っています。この属性値は対応するデータベースの列が許容する最大長に対応します。
NULLを許容する属性やサイズ属性を追加したのは、ウィザードで[表を再生成するための属性]のチェックボックスをOnにしたからです。
@BasicアノテーションはJPA固有のもので、このオプションの属性をfalseに指定すると、このアノテーションが施された属性に対しNULL値でエンティティを永続化できなくなります。
@NotNull 、@Size アノテーションはBean Validationの一部で、Java EE 6で新たに導入されました。
@Size アノテーションを使うと、フィールドの最小・最大長を指定できます(コード1には出ていません)。エンティティの値は対応するデータベースのカラムから取得します。@NotNull アノテーションはアノテーションを付けたフィールドを非NULLにします。@NotNullや@SizeアノテーションはBean Validation仕様の一部であり、適宜Wizardで常に追加されます。@Column(nullableやlength) に対応する属性は「再生成した表の属性」のチェックボックスを指定した場合にのみ追加されます。前者はバリデーションのため、後者はJPAエンティティからデータベース表を再作成するために利用します。
まとめ
JPAとNetBeansを使うと、大抵のコードはNetBeansのWizardが作成してくれますので、アプリケーションのデータ層の開発は非常に簡単であることがおわかり頂けると思います。第2部では、NetBeansでこのアプリケーションの他のレイヤのコードを作成する方法をご紹介します。
参考
- Spring to Java EE Migration Part 2
http://www.oracle.com/technetwork/articles/java/springtojavaee2-1414289.html - Spring to Java EE Migration Part 3
http://www.oracle.com/technetwork/articles/java/springtojavaee3-1569377.html - Spring Framework
http://www.springsource.org/ - NetBeans
http://netbeans.org/ - Java EE 6
http://www.oracle.com/technetwork/java/javaee/overview/index.html
David Heffelfinger
ソフトウェアコンサルタンティングファームであるEnsode Technology, LLCのCTO。
1995年よりアーキテクチャ、デザイン、ソフトウェア開発に従事。1996年からJavaを主要なプログラミング言語として活用し、大規模プロジェクト(アメリカ国土安全保障省、Freddie Mac、Fannie Mae、アメリカ国防総省など)に参画。Southern Methodist Universityソフトウェア工学修士。
0 件のコメント:
コメントを投稿