MVC 1.0ではじめる簡単Java EE開発入門!
この記事の想定読者
今回は、「そもそもJava EEって何?」「なぜJava EEが注目されているの?」ということを解説するとともに、Java EE 8で導入される「MVC 1.0」を利用して、Java EEの技術で簡単な検索Webアプリの開発する方法を解説します。
MVC 1.0について
先日、2本の記事を書きましたので、こちらをご覧ください。
MVC 1.0(Java EE 8)をとりあえず動かしてみた - Java EE 事始め!
JSR 371(MVC 1.0)Early Draftリリース&簡単に解説。 - Java EE 事始め!
これまでのJavaによる開発
これまで、Javaでシステム開発を行う際には、複数のオープンソースフレームワークを組み合わせて使うことが多かったと思います。
たとえば、Struts 1.x + Spring + Hibernate(or MyBatis)は、良く聞く組み合わせです。
しかし、Struts 1.xは2008年からアップデートされておらず、2013年にはサポート終了が公式にアナウンスされました。
Apache Struts 1 EOL Press Release
また、各フレームワークごとに異なる設定ファイル(XMLなど)があります。いわゆる「XML地獄」です。
さらに、複数の異なるフレームワークを組み合わせるためには特殊な設定(XMLやJavaコード)が必要となり、プロジェクトの雛形を作るだけでも相当な工数がかかります。
そもそも、Java EEって?
Java Enterprise Edition(Java EE)は、Webを中心とした大規模システム向けの「標準技術」の「仕様」です。
昔は「J2EE」と呼ばれていて、EJB(Enterprise JavaBeans)の重厚長大さで敬遠されていたようです。
そのアンチテーゼとして、Struts・Spring・Hibernateなどが生まれてきた経緯があります。
しかし、Java EEも負けじと進化を続け、かなり実用的なものになってきました。
2006年にはJava EE 5、2009年にはJava EE 6、2013年にはJava EE 7がリリースされ、実用性や開発の生産性が飛躍的に高まっています。
そして、来年(2016年)の第3四半期(おそらく9月末くらい?)には、Java EE 8が登場する予定です。
Java EEの良いところ
総じていえば、「安心して使えて、生産性が高く、開発が楽」ということです。
フルスタックフレームワーク
Webフレームワーク・DIコンテナ・ORマッパー・その他など、システム開発に必要となるものが一括で提供されています。
また、各技術はシームレスに連携できるようになっており、特殊な設定をする必要はありません。
プロジェクトの雛形作成は非常に楽です。
Java EEによるWebシステムの基本アーキテクチャ
データアクセス
JPA(Java Persistence API)がORマッパーです。データベースにアクセスし、CRUD操作を行います。エンティティクラスでデータをやりとりします。
MVC 1.0 + CDI + JPAで簡単な検索アプリを作る
環境
インストール方法などは、下記の記事をご覧ください。とても楽チンです。
GlassFish 4.0のインストールとNetBeans 8.0の連携 - Java EE 事始め!
作るアプリ
製造者のIDを入力したら、検索結果が表示されるWebアプリです。
プロジェクトの作成
NetBeansで[ファイル]→[新規プロジェクト]→[Maven]→[Webアプリケーション]としてください。
プロジェクトができたら、pom.xmlに下記の依存性を追加します。
<dependencies> <!-- MVC 1.0の実装。まだEarly Draft版 --> <dependency> <groupId>com.oracle.ozark</groupId> <artifactId>ozark</artifactId> <version>1.0.0-m01</version> <scope>compile</scope> </dependency> <!-- JPAの実装 --> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>eclipselink</artifactId> <version>2.5.2</version> <scope>provided</scope> </dependency> <!-- エンティティ自動生成ツール。自動で追記されるので自分で書く必要はなし --> <dependency> <groupId>org.eclipse.persistence</groupId> <artifactId>org.eclipse.persistence.jpa.modelgen.processor</artifactId> <version>2.5.2</version> <scope>provided</scope> </dependency> <!-- OzarkでThymeleafを使うための拡張 --> <dependency> <groupId>com.oracle.ozark.ext</groupId> <artifactId>ozark-thymeleaf</artifactId> <version>1.0.0-m01</version> </dependency> <!-- Java DBのドライバ --> <dependency> <groupId>org.apache.derby</groupId> <artifactId>derbyclient</artifactId> <version>10.11.1.1</version> </dependency> <!-- Java EE 7 Web ProfileのAPI --> <dependency> <groupId>javax</groupId> <artifactId>javaee-web-api</artifactId> <version>7.0</version> <scope>provided</scope> </dependency> </dependencies>
データアクセス
エンティティを作成します。NetBeansを使えば、自動生成が可能です。
ちなみに、「Manufacturer」は「製造者」という意味です。Java DBに最初から入っているテーブルです。
package ozarksample.entity; import java.io.Serializable; import java.util.List; import javax.persistence.*; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; @Entity public class Manufacturer implements Serializable { private static final long serialVersionUID = 1L; @Id @Basic(optional = false) @NotNull @Column(name = "MANUFACTURER_ID") private Integer manufacturerId; @Size(max = 30) private String name; @Size(max = 30) private String addressline1; @Size(max = 30) private String addressline2; ・・・ @OneToMany(cascade = CascadeType.ALL, mappedBy = "manufacturer", fetch = FetchType.LAZY) private List<Product> productList; public Manufacturer() { } // 以下、setter/getterなど
また、エンティティを自動生成すると、同時にperisistence.xmlという、JPAの設定ファイルも自動生成されます。
場所はsrc/main/resources/META-INFです。
自動生成されたXMLに、ユニット名、キャッシュ、ログなどの設定を加えるだけです。
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" ・・・> <!-- あとで呼び出す際のユニット名 --> <persistence-unit name="ozarkPU" transaction-type="JTA"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <jta-data-source>jdbc/sample</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <!-- キャッシュとバリデーションをOFF --> <shared-cache-mode>NONE</shared-cache-mode> <validation-mode>NONE</validation-mode> <properties> <!-- ログ出力の設定など --> <property name="eclipselink.logging.level" value="FINE"/> <property name="eclipselink.logging.thread" value="true"/> <property name="eclipselink.logging.session" value="false"/> <property name="eclipselink.logging.timestamp" value="false"/> <property name="eclipselink.target-database" value="JavaDB"/> </properties> </persistence-unit> </persistence>
ビジネスロジック
package ozarksample.service; import javax.enterprise.context.RequestScoped; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.transaction.Transactional; import ozarksample.entity.Manufacturer; @RequestScoped public class ManufacturerService { @PersistenceContext(unitName = "ozarkPU") private EntityManager manager; @Transactional(Transactional.TxType.REQUIRED) public Manufacturer findById(Integer id) { Manufacturer manufacturer = manager.find(Manufacturer.class, id); return manufacturer; } // other methods for CRUD operations }
EntityManagerがDBアクセスを仲介します。EntityManagerにはCRUD操作用のメソッドが定義されています。また、JPQLという問い合わせ言語、ネイティブSQLの利用も可能です。
@PersistenceContextアノテーションで、EntityManagerをDIします。
クラスについている@RequestScopedは、このクラスをCDI管理対象にするためのものです。次のプレゼンテーション層に、このクラスをDIします。
メソッドに@Transactionalを付けることで、トランザクション管理を自動で行ってくれます。具体的には、対象メソッドにインターセプターをかませることで、メソッド開始前にトランザクションの開始、メソッド終了後にコミット(例外発生時は自動ロールバック)してくれます。
また、ここまでのコードは、プレゼンテーション技術に依存しません。
MVCでもJSFでもJAX-RSでもWebSocketでも、ビジネスロジック以下は変える必要がありません。
そういう意味で、再利用性も高いです。
プレゼンテーション
コントローラーはMVC、ビューは今回はThymeleafで作ります。もちろん、JSPやFacelets(XHTML形式で書く、JSFのビュー)も使えます。
ThymeleafはJava EE標準ではないのですが、独自タグを使わずピュアなHTML5で書けるので、CSSやJavaScriptも書きやすくなり、おすすめです。
ただ、Thymeleafは整形式のXHTMLで書く必要があるので、注意してください。
つまり、<br>はダメで、<br/>ならOKです。
JAX-RSの有効化
MVCは、JAX-RSベースのフレームワークですので、まずはJAX-RSを有効化する必要があります。
そのためには、javax.ws.rs.core.Applicationのサブクラスを作ります。クラスの中身は空でOKです。
package ozarksample; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("api") public class MyApplication extends Application { }
コントローラー
MVCのコントローラーは、ビューの技術に依存しません。これも嬉しいですね。
★追記(2015/04/22)
JSR 371によると、コントローラークラスは、CDI管理ビーンでなければなりません。
下記のコントローラークラスに@RequestScopedが付いているのは、そのためです。
package ozarksample.controller; import javax.enterprise.context.RequestScoped; import javax.inject.Inject; import javax.mvc.Controller; import javax.mvc.Models; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.QueryParam; import ozarksample.entity.Manufacturer; import ozarksample.service.ManufacturerService; @Path("manufacturer") @RequestScoped public class ManufacturerController { @Inject private ManufacturerService manufacturerService; @Inject private Models models; @GET @Path("input") @Controller public String input() { return "manufacturer/input.html"; } @GET @Path("find") @Controller public String findById(@QueryParam("id") Integer id) { Manufacturer manufacturer = manufacturerService.findById(id); models.put("manufacturer", manufacturer); return "manufacturer/result.html"; } }
入力画面(input.html)
HTMLやJSPは、デフォルトでは「WEB-INF/views」配下に作成します。サブフォルダを作ることも可能です。
今回は、WEB-INF/views/manufacturerフォルダ内にHTMLを作成します。
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>input</title> <meta charset="UTF-8"/> </head> <body> <p>input manufacturer id</p> <form action="./find" method="get"> <input type="text" name="id"/> <input type="submit" value="search"/> </form> </body> </html>
検索結果表示画面(result.html)
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>result</title> <meta charset="UTF-8"/> </head> <body> <p th:text="${manufacturer.manufacturerId}">Manufacturer ID</p> <p th:text="${manufacturer.name}">Manufacturer Name</p> </body> </html>
ここまでのまとめ
僕がこのアプリを作るのにかかった時間は、だいたい10分です。
もちろん僕がJava EEやNetBeansに慣れているということもありますが、これくらいのアプリであれば非常に簡単です。
もちろん、CDIやJPAなどの各技術は奥が深いですし、もっと複雑なことをやろうとすると手間はかかるのですが、それはどんなフレームワークでも一緒ではないでしょうか。
ここまでのコードは、GitHubにアップしておきましたので、ご覧ください。
MasatoshiTada/OzarkSample · GitHub
話はそれますが、NetBeansならGitHub連携もすごく簡単です。一切gitコマンドを打たずにコミット・プッシュできます。
NetBeans IDEを使用してGitHubリポジトリを設定するためのビデオ
Java EEの改善してほしい点
さて、ここまで「Java EE良いよね!」という話をしてきましたが、改善点の無いものなどありませんので、いくつか挙げます。
商用アプリケーションサーバーが出るのに時間がかかる
Java EEは仕様が膨大(今回紹介したものは一部に過ぎません)なため、全機能フル対応した商用のアプリケーションサーバーが出てくるのに、だいたい2年くらいかかっています。
ただ、商用サポートはありませんが、オープンソースのGlassFishやWildFly(旧JBossのオープンソース版)は、タイムリーに出てきます。
特にGlassFishはOracleが開発しているため、Java EEの正式リリースと同時に出されます。
また、Oracle WebLogic Serverは、EE 7あたりから、少しずつEEの仕様に対応する「段階的対応」を取っています。
Java EE 8は2016年秋リリース予定ですが、特にMVC 1.0はEE 8の目玉機能ですので、WebLogicは真っ先に対応するのではないでしょうか(と、期待しています^^;)。
仕様の進化がスロー
僕の私見ですが、進化の速さ・生産性の高さ・機能の豊富さは、まだSpringや.NETの方が優位だと思います。
これは、Springや.NETは特定企業(Pivotal社・Microsoft社)が主体で開発を進めるのに対して、Javaはコミュニティによる合議制で仕様の策定・開発を進めている点が大きいのでしょう。
ただ、これはどちらが良いとは一概には言えないと思います。
Javaは後方互換性を大切にしていますし、標準技術による安定性・信頼性を求めるのであれば、Java EEがいいのではないでしょうか。場合によりけり、だと思います。
ただ、最低でも、新規開発でStruts 1.xを使い続けるのは、止めたほうがよいでしょうね(^^;
最後に
長い記事にお付き合いいただき、ありがとうございました。
この記事を読んだ方に、「思ってたよりだいぶ簡単じゃん!Java EEちょっとやってみようかな~」と思っていただければ嬉しいです。
また、4/11(土)のJJUG CCCに、スピーカーとしての登壇が決まりました!
CCCへの登壇は、3回連続3回目になりました。
タイトルは「はまる!JPA(初学者向けライト版)」です。
JPAは初めてという方が対象ですので、他のORマッパーは使ったことあるけど、JPAは全く知らないという方でも大丈夫です。
Sessions / JJUG CCC 2015 Spring(4月11日開催) | 日本Javaユーザーグループ
過去2回の発表資料はこちらです。
JPAの同時実行制御とロック20140518 #ccc_r15 #jjug_ccc
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
それでは、Enjoy Java EE!