JPA + EJB + JSF による Web アプリケーション(その 3)
そろそろ現実に帰って Java EE の記事を書きます(汗)。
Model の Entity を JPA で、Business logic を EJB で実装します。JPA は前にも説明した通り永続化と O/R マッピングですが、EJB をことさらに使う理由としては、WildFly (やその他 Java EE アプリケーションサーバ)が EJB コンテナを持っていることにより、アノテーション一発でロジックを呼び出せるという利点があるからです。
以下、ソースコードを書いていきます。ファイルの配置については下図を参照のこと。
package jp.mydns.akanekodou.entity; import java.io.Serializable; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; @SuppressWarnings("serial") @Entity public class District implements Serializable { @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 javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.GeneratedValue; import javax.persistence.ManyToOne; @SuppressWarnings("serial") @Entity public class Prefecture implements Serializable { @Id @GeneratedValue private int id; private String name; @ManyToOne private District district; public Prefecture() { } public Prefecture( String name, District district ) { this.name = name; 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 District getDistrict() { return district; } public void setDistrict(District district) { this.district = district; } }
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; @SuppressWarnings("serial") @Entity public class City implements Serializable { @Id @GeneratedValue private int id; private String name; @ManyToOne private Prefecture prefecture; private Date designated; private double area; private int population; public City() { } public City( String name, Prefecture prefecture, Date designated, double area, int population ) { this.name = name; this.prefecture = prefecture; this.designated = designated; this.area = area; this.population = population; } 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 Prefecture getPrefecture() { return prefecture; } public void setPrefecture(Prefecture prefecture) { this.prefecture = prefecture; } 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; } }
JPA がこれらを認識できるように persistence.xml に記述します。
<?xml version="1.0"?> <persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd" version="2.1"> <persistence-unit name="cityManager"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/PostgreSQL/mydb</jta-data-source> <class>jp.mydns.akanekodou.entity.City</class> <class>jp.mydns.akanekodou.entity.Prefecture</class> <class>jp.mydns.akanekodou.entity.District</class> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" /> </properties> </persistence-unit> </persistence>
続いて EJB で Business logic を書きます。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); }
今回は EJB とそれを利用するプログラムを両方とも同じサーバに配置するので Local
Bean です。別々のサーバに配置する場合は Remote
Bean になります。
インターフェースですので実装を書く必要がありますね。
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); } }
EntityManager
をアノテーション一発で生成出来るのも Java EE アプリケーションサーバの強みです。非常に記述が簡潔になります。Session Bean には Staleless
なものと Staleful
なものがありますが、セッションに渡って連続的な処理をする場合は Staleful
、一メソッド内で完結する処理なら Staleless
と覚えると良いでしょう。
なお今回はインターフェースと実装を分けましたが、EJB 3.1 からは特定のインターフェース(java.io.Serializable
, java.io.Externalizable
及び javax.ejb
パッケージに含まれるインターフェース)以外を implements
していないことと LocalBean
アノテーションを付けることを条件にインターフェースを省略できるようです。なので、実は今回について言えばインターフェースを省略してこういう書き方もできる。
package jp.mydns.akanekodou.dao; import javax.ejb.Stateless; import javax.ejb.LocalBean; import javax.persistence.PersistenceContext; import javax.persistence.EntityManager; import javax.persistence.Query; import java.util.List; import jp.mydns.akanekodou.entity.City; @Stateless @LocalBean public class CityDAO { @PersistenceContext private EntityManager manager; public List<City> all() { Query query = manager.createQuery("from City"); @SuppressWarnings("unchecked") List<City> result = query.getResultList(); return result; } public City find(int id) { return manager.find(City.class, id); } }
試したらちゃんと動きました。でも迷ったらインターフェースと実装に分けて書く方が確実です。
次回は JSF 側のプロジェクトを作ります。