JavaEEでWebアプリを高速開発する手順をご紹介するエントリの第2弾です。前回の記事はこちら。
今回はいよいよWebアプリを作っていきます。
このエントリのゴール
- 簡単にJavaEE開発環境を構築する手順を説明します【前回ご紹介】
- JavaEEを使ったRDBにアクセスする簡単なサンプルを作成する手順を説明します【今回はこちらの手順をご紹介】
前提
- 前回のエントリに沿って開発環境が構築されていること
アプリケーションの作成
JavaEEの仕様は重厚長大ですが、Webアプリを作るだけならほんの少しのことを知っているだけでOKです。
ここでは、JPAを使って単一テーブルからのCRUDを実行するシンプルな1画面のみのアプリを作ってみます。画面イメージは以下のようになります。
それでは、EJB→Servlet→JSPの順番で作成していきます。
EJB
EJBといっても大げさに考える必要はなく、DBアクセスを担当するフツーのクラスのことです。
今回は、JavaEE標準のDBアクセス仕様であるJPAを使ってEJBを実装します。まずはJPAの動作に必要な設定ファイルとクラスを作成しましょう。
JPA設定ファイル(persistence.xml)の作成
Eclipse上にソースフォルダsrc/main/resourcesが無い場合は作成し、更にその中にMETA-INFフォルダを作って下さい。
META-INFフォルダの中にpersistence.xmlを次の通りに作成します。
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="pu" transaction-type="JTA"> <jta-data-source>jdbc/App</jta-data-source> <exclude-unlisted-classes>false</exclude-unlisted-classes> <properties> <property name="eclipselink.ddl-generation" value="create-tables" /> <property name="eclipselink.logging.level.sql" value="FINE" /> <property name="eclipselink.logging.parameters" value="true" /> <!-- eclipseのConsoleビューにSQLを出力するための設定. http://stackoverflow.com/questions/4676705/jpa-2-0-logging-and-tracing-through-with-glassfish-3-0-1-and-netbeans-6-9-1 --> <property name="eclipselink.logging.logger" value="org.eclipse.persistence.logging.DefaultSessionLog"/> </properties> </persistence-unit> </persistence>
<property name=”eclipselink.ddl-generation” value=”create-tables” />という記述のおかげで、サーバ起動時にテーブルが自動で作られます。
また、DBデータはプロジェクト内のdbフォルダの中に作られます。このフォルダを削除すれば、再度テーブルが作られます。
エンティティクラスの作成
DBのテーブルに対応するクラスを作成しましょう。
src/main/java下のパッケージsandbox.entityに以下の通りにEmployeeクラスを作成します。
package sandbox.entity; import java.io.Serializable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; @Entity public class Employee implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) Long id; @Column(nullable = false, unique = true, length = 100) String name; public Long getId() { return this.id; } public String getName() { return this.name; } public void setId(final Long pId) { this.id = pId; } public void setName(final String pName) { this.name = pName; } }
EJBクラスの作成
src/main/java下のパッケージsandbox.serviceにEmployeeServiceクラスを作成します。INSERT、SELECT、DELETE用に3つのメソッドを作成しましょう。
package sandbox.service; import java.util.List; import javax.ejb.Stateless; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import sandbox.entity.Employee; @Stateless public class EmployeeService { @PersistenceContext(unitName = "pu") EntityManager em; public Employee delete(final long pDeleteEmployeeId) { final Employee emp = this.em.find(Employee.class, Long.valueOf(pDeleteEmployeeId)); this.em.remove(emp); return emp; } public List<Employee> getAll() { final CriteriaBuilder builder = this.em.getCriteriaBuilder(); final CriteriaQuery<Employee> query = builder.createQuery(Employee.class); query.from(Employee.class); return this.em.createQuery(query).getResultList(); } public Employee insert(final String pName) { final Employee newEmp = new Employee(); newEmp.setName(pName); this.em.persist(newEmp); return newEmp; } }
たったこれだけです。メソッドの前後で自動的にトランザクションが仕込まれます。また以前の記事「依存性注入(DIコンテナ)について」で紹介したDI機能によって、JPA用のEntityManagerオブジェクトが良い感じでインジェクトされます。
またSQLが全く登場しないことにも注目して下さい。JPAのCriteria Queryという機能を使うと、SQLを書かずに済むシーンが増えます。文字列に頼らず型を使ってDBアクセス出来るようになることは、Javaの型安全性ととても相性の良い手法だと思います。JPAを使う場合は、ぜひ有効活用してみて下さい。
これら全ての機能がJavaEEによって提供されている点にも注目です。SpringやHibernateなどのライブラリを組み合わせて使う場合、ライブラリ同士をうまく接続するために多少なりとも設定を書く必要がありますが、JavaEEであればライブラリを、それもたった2つのJARを参照するだけ。
このお手軽さがJavaEEを使って開発する際の魅力です。
Servletの作成
続いてHTTPリクエストを受け取るためのServletを作成します。今回は、ルート( / )へのGETで社員一覧、ルートへのPOSTで社員の追加を行うようにしましょう。
src/main/java下のパッケージsandbox.web.handlerにEmployeeHandlerクラスを作成します。
package sandbox.web.handler; import java.io.IOException; import java.util.List; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sandbox.entity.Employee; import sandbox.service.EmployeeService; @WebServlet(urlPatterns = "/") public class EmployeeHandler extends HttpServlet { @Inject EmployeeService employeeService; @Override protected void doGet(final HttpServletRequest pReq, final HttpServletResponse pResp) throws ServletException, IOException { final List<Employee> emps = this.employeeService.getAll(); pReq.setAttribute("emps", emps); pReq.getRequestDispatcher("WEB-INF/emp.jsp").forward(pReq, pResp); } @Override protected void doPost(final HttpServletRequest pReq, final HttpServletResponse pResp) throws ServletException, IOException { final String empName = pReq.getParameter("employee-name"); this.employeeService.insert(empName); pResp.sendRedirect("/"); } }
もう1つServletを作ります。/deleteへのPOSTで社員一覧を削除するようにしましょう。
src/main/java下のパッケージsandbox.web.handlerにEmployeeDeleteHandlerクラスを作成します。
package sandbox.web.handler; import java.io.IOException; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import sandbox.service.EmployeeService; @WebServlet(urlPatterns = "/delete") public class EmployeeDeleteHandler extends HttpServlet { @Inject EmployeeService employeeService; @Override protected void doPost(final HttpServletRequest pReq, final HttpServletResponse pResp) throws ServletException, IOException { final String employeeIdStr = pReq.getParameter("employee-id"); try { final long employeeId = Long.parseLong(employeeIdStr); this.employeeService.delete(employeeId); } catch (final NumberFormatException e) { // nop } pResp.sendRedirect("/"); } }
各メソッドでやっていることは
- EJBを呼び出してDB操作
- JSP呼び出し、または/へのリダイレクト
という単純な処理です。
ServletクラスでもDI機能が使えるため、EmployeeServiceオブジェクトが良い感じでインジェクトされます。
JSPの作成
最後に、JSPで画面を作成しましょう。
src/main/webapp/WEB-INFの下にemp.jspを作成して下さい。
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet"> <title>社員情報</title> </head> <body> <div class="container"> <fieldset> <legend>社員一覧</legend> <table class="table table-striped"> <thead> <tr> <td>#</td> <td>名前</td> <td> </td> </tr> </thead> <tbody> <c:forEach items="${emps}" var="emp"> <tr> <td>${emp.id}</td> <td>${emp.name}</td> <td> <form method="post" action="${request.contextPath}/delete"> <input type="hidden" value="${emp.id}" name="employee-id" /> <button class="btn btn-danger"><span class="glyphicon glyphicon-trash"></span></button> </form> </td> </tr> </c:forEach> </tbody> </table> </fieldset> <fieldset> <legend>社員登録</legend> <form method="post" action="${request.contextPath}"> <div class="form-group"> <input type="text" name="employee-name" class="form-control" /> </div> <button class="btn btn-primary">追加</button> </form> </fieldset> </div> </body> </html>
手軽に見栄えを良くするために、Bootstrapを使っています。
文字化け防止クラスの作成
実はこのままでは日本語が文字化けします。次のクラスを作成して文字コードをUTF-8に統一します。
src/main/java下のパッケージsandbox.webにCharacterEncodingFilterクラスを作成します。
package sandbox.web; import java.io.IOException; import java.nio.charset.StandardCharsets; import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebFilter(urlPatterns = "/*", dispatcherTypes = DispatcherType.REQUEST) public class CharacterEncodingFilter implements Filter { @Override public void destroy() { // nop } @Override public void doFilter(final ServletRequest pRequest, final ServletResponse pResponse, final FilterChain pChain) throws IOException, ServletException { ((HttpServletRequest) pRequest).setCharacterEncoding(StandardCharsets.UTF_8.name()); ((HttpServletResponse) pResponse).setCharacterEncoding(StandardCharsets.UTF_8.name()); pChain.doFilter(pRequest, pResponse); } @Override public void init(final FilterConfig pFilterConfig) throws ServletException { // nop } }
以上でコーディングは終了です!おつかれさまでした。
Eclipseプロジェクトは次のような状態になっているはずです。
動作確認
前回作成したJavaEEContainerStarterクラスを起動した上で、以下のURLにアクセスして動作を確認してみて下さい。
まとめ
Eclipse上でJavaEEを使った開発の手順を説明してきました。
RoRほどではないものの、思ったよりも簡単に開発を始められ、割りと少ないコード量でWebアプリが作成出来ることがご理解いただけたかと思います。
フルJavaEEを使って開発する利点は、機能が豊富で、更にそれらの機能が既に統合されている、という点です。新しい機能を使うときにJAR参照を追加したりする必要はありません。RESTfulなWebアプリを作るためのJAX-RSであっても、WebSocketのハンドラクラスであっても、適切なアノテーションを付与したクラスを作れば、それだけで動作します。またビジネスロジックをEJBとして作成すれば、DI機能によってどこからでも利用出来ます。
しかしこう言った利点を享受したくてJavaEEを使おうとしても、環境構築が面倒で挫折した、という人は少なくないと思います。
今回ご紹介した環境構築手順を使えば、簡単高速に開発が始められますので、ぜひJavaEEを体験してその便利さに触れていただきたいと思います。