JSF 2.0で作るWebアプリケーション 前編

id:okazuki氏のリクエストに答えて。

本当はJava EE 6フルスタックなWebアプリケーションを作るのがトランザクション管理等もしてくれて一番簡単なのですが、JSF 2.0だけに注力していただきたいのでここはTomcat 6 + Mojarra 2.0(JSF実装) + EclipseLink 2.0(JPA実装)というJava EE 5 なサーブレットコンテナ上で動かします。JSF 2.0はJava EE 6未対応でも問題なく動くのがポイントが高いです。JavaEE 5ではELメソッドは動きませんが、既存の環境で動かせるのは大きいです。

開発はNetBeans 6.8ベータで行っていますが、NetBeansCRUDアプリ自動生成機能等はつかいませんのでEclipseでも同じです。クラスの新規作成さえできれば環境はといません。

web.xmlの設定

通常IDEによって設定されるのであんまりいじる必要はありません。

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>/faces/*</url-pattern>
    </servlet-mapping>
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <welcome-file-list>
        <welcome-file>faces/index.xhtml</welcome-file>
    </welcome-file-list>
</web-app>

javax.faces.webapp.FacesServletというサーブレットを登録してるだけですね。

NetBeansなら以下のように表示されます。
http://shin.cside.com/diary/2009/1030-05.png

データベースの作成

まずデータベースを作ります。

このあたりを参考にすると必要な項目は

  • ID
  • 従業員名
  • 入社年月日
  • 給料

の4つのようです。

セットアップが必要ないJDKに付属するJavaDB(Derby)を利用します。

あらかじめ起動しておいてください。NetBeansならサービスウインドウから起動や停止、データベースの作成が可能です。

http://shin.cside.com/diary/2009/1030-01.png


データベースも作成しておきましょう。「Employee」とでもしておきましょう。ログインIDとパスワードはともに「hoge」とでもしておきます。テーブルは作る必要はありません。


このシステムで使用するライブラリは以下のようになります。依存ライブラリが少ないというのは非常にいいですね。
http://shin.cside.com/diary/2009/1030-02.png

エンティティの作成

いつもならDBを先に作成しておいてからエンティティクラスの自動生成をするのですが、今回は珍しくエンティティクラスから作成してみます。

package entity;

import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;

@Entity
@Table(name="employee")
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name="name")
    private String 従業員名;

    @Column(name="entDate")
    @Temporal(javax.persistence.TemporalType.DATE)
    private Date 入社日;

    @Column(name="salary")
    private Integer 給料;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Date get入社日() {
        return 入社日;
    }

    public void set入社日(Date 入社日) {
        this.入社日 = 入社日;
    }

    public String get従業員名() {
        return 従業員名;
    }

    public void set従業員名(String 従業員名) {
        this.従業員名 = 従業員名;
    }

    public Integer get給料() {
        return 給料;
    }

    public void set給料(Integer 給料) {
        this.給料 = 給料;
    }

    @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 Employee)) {
            return false;
        }
        Employee other = (Employee) 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 "entity.Employee[id=" + id + "]";
    }

}

persistence.xmlを作成します。このへんは一から作成するのではなく、IDEの機能でやったほうが楽でしょう。生成したコードは以下の通り。

<?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="EmployeePU" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>entity.Employee</class>
    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <shared-cache-mode>NONE</shared-cache-mode>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/Employee"/>
      <property name="javax.persistence.jdbc.password" value="hoge"/>
      <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
      <property name="javax.persistence.jdbc.user" value="hoge"/>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
    </properties>
  </persistence-unit>
</persistence>

NetBeansで開くとわかりやすいですね。
http://shin.cside.com/diary/2009/1030-03.png
つまり、毎回立ち上げるたびにテーブルが削除されて作成されます。開発中はこれやテーブルの自動生成をいれておくとエンティティクラスから自動でテーブルが作られるので便利です。

サービスの作成

とりあえず今回は何も外部のライブラリを使用していないのでシンプルなものを作成します。一覧、新規、変更、削除を実装しています。

package service;

import entity.Employee;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

@ManagedBean(eager=true)
@ApplicationScoped
public class EmployeeService  implements Serializable{

    private EntityManagerFactory emf = Persistence.createEntityManagerFactory("EmployeePU");
    
    @PostConstruct
    public void init() {
        System.out.println("つくられた:"+emf);
        for(int i=0;i<10;i++){
            Employee emp = new Employee();
            emp.set従業員名("ほげ"+i);
            Calendar c = new GregorianCalendar(2000, i, i);
            emp.set入社日(c.getTime());
            emp.set給料(150000+(i*10000));
            insert(emp);
        }
    }



    public List<Employee> findAll(){
        EntityManager em = emf.createEntityManager();
        try{
            return em.createQuery("select e from Employee e order by e.id").getResultList();
        }finally{
            em.close();
        }
    }

    public void insert(Employee employee){
        EntityManager em = emf.createEntityManager();
        try{
            em.getTransaction().begin();
            em.persist(employee);
            em.getTransaction().commit();
        }finally{
            em.close();
        }
    }

    public void update(Employee employee){
        EntityManager em = emf.createEntityManager();
        try{
            em.getTransaction().begin();
            em.merge(employee);
            em.getTransaction().commit();
        }finally{
            em.close();
        }
    }

    public void delete(Employee employee){
        EntityManager em = emf.createEntityManager();
        try{
            em.getTransaction().begin();
      em.remove(em.getReference(Employee.class, employee.getId()));
            em.getTransaction().commit();
        }finally{
            em.close();
        }

    }
}

Applicationスコープですのでシングルトンとイコールだと思ってよいです。このインスタンスが作られるのはデフォルトでは必要なタイミングになってからですが、起動時にロードさせたいため「@ManagedBean(eager=true)」と指定しています。これで起動直後にサンプルデータが10件作られるようになります。




データベースを見てみます。
http://shin.cside.com/diary/2009/1030-04.png
連番用のテーブルとあわせて従業員テーブルが作られていますね。

これでデータアクセス周りは完成しました。