JPA + EJB + JSF による Web アプリケーション(その 8)
これまでの総仕上げとして、一からテーブルを作って JPA + EJB + JSF で Web アプリケーションを作成してみましょう。
テーブルの作成
今回は RDBMS として PostgreSQL を使います。
CREATE TABLE district ( id INT PRIMARY KEY, name VARCHAR(30) ); INSERT INTO district ( id, name ) VALUES (1,'北海道'); INSERT INTO district ( id, name ) VALUES (2,'東北'); INSERT INTO district ( id, name ) VALUES (3,'関東'); INSERT INTO district ( id, name ) VALUES (4,'中部'); INSERT INTO district ( id, name ) VALUES (5,'近畿'); INSERT INTO district ( id, name ) VALUES (6,'中国'); INSERT INTO district ( id, name ) VALUES (7,'四国'); INSERT INTO district ( id, name ) VALUES (8,'九州'); INSERT INTO district ( id, name ) VALUES (9,'沖縄'); CREATE TABLE city ( id INT PRIMARY KEY, name VARCHAR(30), pref VARCHAR(30), designated DATE, area DECIMAL(7,2), population INT, district_id INT REFERENCES district(id) ); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (1,'札幌市','北海道','1972-04-01',1121.12,1921245,1); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (2,'仙台市','宮城県','1989-04-01',785.85,1049493,2); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (3,'さいたま市','埼玉県','2003-04-01',217.49,1229479,3); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (4,'千葉市','千葉県','1992-04-01',272.08,963120,3); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (5,'横浜市','神奈川県','1956-09-01',437.38,3691693,3); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (6,'川崎市','神奈川県','1972-04-01',142.7,1430773,3); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (7,'相模原市','神奈川県','2010-04-01',328.83,719412,3); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (8,'新潟市','新潟県','2007-04-01',726.1,812458,4); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (9,'静岡市','静岡県','2005-04-01',1411.85,714513,4); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (10,'浜松市','静岡県','2007-04-01',1558.04,798924,4); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (11,'名古屋市','愛知県','1956-09-01',326.43,2266517,4); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (12,'京都市','京都府','1956-09-01',827.9,1473416,5); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (13,'大阪市','大阪府','1956-09-01',223.0,2670579,5); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (14,'堺市','大阪府','2006-04-01',149.99,842685,5); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (15,'神戸市','兵庫県','1956-09-01',552.26,1544496,5); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (16,'岡山市','岡山県','2009-04-01',789.92,710913,6); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (17,'広島市','広島県','1980-04-01',905.41,1177711,6); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (18,'北九州市','福岡県','1963-04-01',488.78,974287,8); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (19,'福岡市','福岡県','1972-04-01',341.7,1479433,8); INSERT INTO city ( id, name, pref, designated, area, population, district_id ) VALUES (20,'熊本市','熊本県','2012-04-01',389.54,736010,8);
エンティティの作成
package jp.mydns.akanekodou.entity; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; @Entity public class District implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private int id; private String name; public District() { } public District(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
package jp.mydns.akanekodou.entity; import java.io.Serializable; import java.util.Date; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.ManyToOne; @Entity public class City implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue private int id; private String name; private String pref; private Date designated; private double area; private int population; @ManyToOne private District district; public City() { } public City( String name, String pref, Date designated, double area, int population, District district ) { this.name = name; this.pref = pref; this.designated = designated; this.area = area; this.population = population; this.district = district; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPref() { return pref; } public void setPref(String pref) { this.pref = pref; } public Date getDesignated() { return designated; } public void setDesignated(Date designated) { this.designated = designated; } public double getArea() { return area; } public void setArea(double area) { this.area = area; } public int getPopulation() { return population; } public void setPopulation(int population) { this.population = population; } public District getDistrict() { return district; } public void setDistrict(District district) { this.district = district; } }
今回は city
テーブルの district_id
カラムが district
テーブルの id
カラムを参照している(命名規則に従っている)ので、JoinColumn
アノテーションは省略できます。
<?xml version="1.0"?> <persistence 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" version="2.0"> <persistence-unit name="cityManager"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <jta-data-source>java:/pgsql/mydb</jta-data-source> <class>jp.mydns.akanekodou.entity.City</class> <class>jp.mydns.akanekodou.entity.District</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> </properties> </persistence-unit> </persistence>
必要な data-source はあらかじめ定義済みであるものとします。
DAO 層の実装(EJB 使用)
package jp.mydns.akanekodou.dao; import javax.ejb.Local; import java.util.List; import jp.mydns.akanekodou.entity.City; @Local public interface CityDAO { List<City> all(); City find(int id); }
package jp.mydns.akanekodou.dao; import javax.ejb.Stateless; import javax.persistence.PersistenceContext; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.List; import jp.mydns.akanekodou.entity.City; @Stateless public class CityDAOImpl implements CityDAO { @PersistenceContext private EntityManager manager; @Override public List<City> all() { Query query = manager.createQuery("from City"); @SuppressWarnings("unchecked") List<City> result = query.getResultList(); return result; } @Override public City find(int id) { return manager.find(City.class, id); } }
管理 Bean の作成
package jp.mydns.akanekodou; import javax.ejb.EJB; import javax.annotation.PostConstruct; import javax.faces.bean.ManagedBean; import javax.faces.component.html.HtmlInputHidden; import java.util.List; import jp.mydns.akanekodou.dao.CityDAO; import jp.mydns.akanekodou.entity.City; @ManagedBean public class MajorCity { @EJB private CityDAO dao; private HtmlInputHidden id; private List<City> items; private City item; @PostConstruct private void init() { items = dao.all(); } public HtmlInputHidden getId() { return id; } public void setId(HtmlInputHidden id) { this.id = id; } public List<City> getItems() { return items; } public City getItem() { return item; } public String detail() { int id = (Integer)this.id.getValue(); item = dao.find(id); return "detail"; } }
Facelets と CSS の作成
list.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>日本の政令指定都市一覧</title> <h:outputStylesheet library="css" name="list.css" /> </h:head> <h:body> <h:dataTable var="item" value="#{majorCity.items}" columnClasses="no,pref,name,button" rowClasses="tr0,tr1"> <f:facet name="caption">日本の政令指定都市一覧</f:facet> <h:column> <f:facet name="header">No.</f:facet> <h:outputText value="#{item.id}" /> </h:column> <h:column> <f:facet name="header">都道府県</f:facet> <h:outputText value="#{item.pref}" /> </h:column> <h:column> <f:facet name="header">都市名</f:facet> <h:outputText value="#{item.name}" /> </h:column> <h:column> <f:facet name="header"></f:facet> <h:form> <div> <h:commandButton action="#{majorCity.detail}" value="詳細" /> <h:inputHidden value="#{item.id}" binding="#{majorCity.id}" /> </div> </h:form> </h:column> </h:dataTable> </h:body> </html>
h:dataTable
の columnClasses
属性は、表の列ごとの class
属性値を指定します。また rowClasses
は行ごと(tr
要素)の class
属性値を指定します。複数書いておくことで行ごとに tr0
, tr1
が繰り返し指定されます。
detail.xhtml
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>#{majorCity.item.name}の詳細</title> <h:outputStylesheet library="css" name="detail.css" /> </h:head> <h:body> <h:panelGrid columns="2" columnClasses="header,"> <f:facet name="caption">#{majorCity.item.name}のデータ</f:facet> <h:outputText value="指定日" /> <h:outputText value="#{majorCity.item.designated}"> <f:convertDateTime pattern="yyyy 年 M 月 d 日" /> </h:outputText> <h:outputText value="地方" /> <h:outputText value="#{majorCity.item.district.name}" /> <h:outputText value="都道府県" /> <h:outputText value="#{majorCity.item.pref}" /> <h:outputText value="面積" /> <h:panelGroup> <h:outputText value="#{majorCity.item.area}"> <f:convertNumber /> </h:outputText>km² </h:panelGroup> <h:outputText value="人口" /> <h:panelGroup> <h:outputText value="#{majorCity.item.population}"> <f:convertNumber /> </h:outputText>人 </h:panelGroup> </h:panelGrid> <p><h:link value="一覧に戻る" outcome="list" /></p> </h:body> </html>
f:convertDateTime
で日付のフォーマット、f:convertNumber
で数値のフォーマットを行っています。
table { margin : auto; border : 1px outset black } caption { font-size : 20pt; color : #008b8b } thead { background-color : #008b8b; color : #e6e6fa; font-size : 14pt } th { border : 1px inset black } tr.tr0 { background-color : #b0e0e6 } tr.tr1 { background-color : #e6e6fa } td { text-align : left; border : 1px inset black } .no { width : 30px } .pref { width : 100px } .name { width : 120px } .button { width : 80px; text-align : center; }
table { margin : auto } caption { font-size : 20pt; color : #6a5acd } td { width : 180px; background-color : #e6e6fa; color : #6a5acd; font-size : 14pt } td.header { width : auto; background-color : #6a5acd; color : #e6e6fa; font-weight : bold } p { text-align : center }
この他、list.jsf にリダイレクトするためのダミーの index.jsp と web.xml の修正(filter や welcome-file の設定など)をして完成です。