S2Hibernate-JPA-rc-1.0.0 の サンプル

前回のつづきです。今回はサンプルを作成します。
定番のDepartmentとEmployeeのone-to-many/many-to-one関連でやってみます。RDBMSSeasar2に含まれているHSQLDB(hsqldb-1.8.0.1.jar)を使います。流れはこんな感じ。

  1. 環境のセットアップ
  2. テスト作成(環境が整ったことをテストする)
  3. テーブル作成と初期データ投入
  4. エンティティクラス作成
  5. DAO作成
  6. テスト作成(DAOやエンティティのマッピングが正しいことをテストする)

環境のセットアップ

IDEにはEclipseを使いますが、Eclipseのプロジェクト参照はなしで話を進めたいと思います。S2Hibernate-JPA-rc-1.0.0はMaven2に対応していますが、Maven2も使わないで進めます。

  1. Eclpseで適当にJavaプロジェクトを作ります。ここでは「s2hibernate-jpa-example」という名前にします。JREは1.5を使用するようにしてください。
  2. 以下のものを http://s2container.seasar.org/ja/http://s2hibernate.seasar.org/ja/ からダウンロードします。
  3. クラスパスを設定します。
  4. jdbc.dionを開き、xaDataSourceコンポーネントのURLプロパティに"jdbc:hsqldb:hsql://localhost:9001"を設定します。コメントアウトさているものを使用すればOKです。
  5. SMART deploy の設定をします。
    • 今回の例では"example"をルートパッケージとします。convention.diconを開き、initMethodであるaddRootPackageNameのarg要素に"example"と指定してください。"org.seasar.framework.container.warmdeploy"を"example"に置き換えればOKです。
    • s2container.diconを用意します。今回はWARM deployを使います。設定内容はちょっと下を見てください。
  6. META-INF/persistence.xmlを開き、jta-data-source要素に指定された"jdbc/DataSource"を"jdbc/dataSource"に変更します。(これはS2Hibernate-JPAが提供するpersistence.xmlの不具合です。次のバージョンでは、はじめから"jdbc/dataSource"で提供します。
  7. S2Container 2.4.8のzipファイルに含まれるseasar2/hsql/bin/runHsqldb.batを実行し、HSQLDBを起動します。
s2container.dicon

手順5のs2container.diconの設定では次のような定義をします。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
    "http://www.seasar.org/dtd/components24.dtd">
<components>
    <include path="warmdeploy.dicon"/>
</components>

環境のセットアップは以上です。

テスト作成(環境が整ったことをテストする)

環境が整ったことを確認するために、ExampleTestをルートパッケージ直下につくり、このテストクラスでS2ContainerからEntityManagerを取得できる(自動フィールドバインディングが行われる)ことを確認してみます。テスティングツールにはSeasar2を使ったTestで最もポピュラーなS2TestCaseを使います。

package example;

import javax.persistence.EntityManager;

import org.seasar.extension.unit.S2TestCase;

public class ExampleTest extends S2TestCase {

  private EntityManager em;

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    include("jpa.dicon");
  }

  public void test() throws Exception {
    assertNotNull(em);
  }
}

このテストが失敗したらどこかの設定がおかしいことになります。

テーブル作成と初期データ投入

HSQLDBにテーブルとデータを作ります。

  1. seasar2/hsql/bin/runHsqldbManager.batを実行しHSQL Database Managerを開きます。
    • 開かれたダイアログのTypeのコンボボックスで「HSQL Database Engine Server」を選択します。
    • URLのテストボックスに「jdbc:hsqldb:hsql://localhost:9001」と入力します。
  2. HSQL Database Managerで次のCREATE TABLE と INSERT を実行します。
CREATE TABLE DEPARTMENT
(
  ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY ,
  NAME VARCHAR
)

CREATE TABLE EMPLOYEE
(
  ID INTEGER GENERATED BY DEFAULT AS IDENTITY(START WITH 1) NOT NULL PRIMARY KEY ,
  NAME VARCHAR,
  HEIGHT INTEGER,
  WEIGHT INTEGER,
  DEPARTMENT_ID INTEGER NOT NULL, 
  CONSTRAINT FK_DEPARTMENT_ID FOREIGN KEY(DEPARTMENT_ID) REFERENCES DEPARTMENT(ID)
)

INSERT INTO DEPARTMENT VALUES(1, 'Account')
INSERT INTO DEPARTMENT VALUES(2, 'Sales')

INSERT INTO EMPLOYEE VALUES(1, 'simagoro', 168, 72, 1)
INSERT INTO EMPLOYEE VALUES(2, 'gochin', 161, 60, 1)
INSERT INTO EMPLOYEE VALUES(3, 'maki', 155, 52, 2)

メニューのViewからRefresh Treeを選択し実行すると左側のViewに追加したテーブルが表示されます。

エンティティクラス作成

テーブルに対応するエンティティクラスを作成します。SMART deployの規約から自動検出できるように、エンティティクラスはエンティティパッケージ(ルートパッケージ + entity)に配置する必要があります。エンティティパッケージは、この例では example.entity になります。

Departmentクラス
package example.entity;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;

@Entity
public class Department {

  @Id
  @GeneratedValue
  private Integer id;

  private String name;

  @OneToMany(mappedBy = "department")
  private Set<Employee> employees = new HashSet<Employee>();

  public Integer getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Set<Employee> getEmployees() {
    return employees;
  }

  public void setEmployees(Set<Employee> employees) {
    this.employees = employees;
  }
}
Employeeクラス
package example.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;

@Entity
public class Employee {

  @Id
  @GeneratedValue
  private Integer id;

  private String name;

  private Integer height;

  private Integer weight;

  @ManyToOne
  private Department department;

  public Department getDepartment() {
    return department;
  }

  public void setDepartment(Department department) {
    this.department = department;
  }

  public Integer getHeight() {
    return height;
  }

  public void setHeight(Integer height) {
    this.height = height;
  }

  public Integer getId() {
    return id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Integer getWeight() {
    return weight;
  }

  public void setWeight(Integer weight) {
    this.weight = weight;
  }
}

DAO作成

DAOのインタフェースと実装をSMART deployの規約に則って適切なパッケージに配置します。
この例ではルートパッケージ名が exampleなので DAOのインタフェースと実装のパッケージはそれぞれ example.dao と example.dao.impl になります。
EmployeeDaoにはEntityManagerのsetterがあるのでEntityManagerがDIされます。EmployeeDaoはEntityManagerからエンティティを取得し返します。Seasar2APIには依存していません。

package example.dao;

import example.entity.Employee;

public interface EmployeeDao {
  Employee find(Integer id);
}
package example.dao.impl;

import javax.persistence.EntityManager;

import example.dao.EmployeeDao;
import example.entity.Employee;

public class EmployeeDaoImpl implements EmployeeDao {

  private EntityManager em;

  public void setEntityManager(EntityManager em) {
    this.em = em;
  }

  public Employee find(Integer id) {
    return em.find(Employee.class, id);
  }
}

テスト作成(DAOやJPAマッピングが正しいことをテストする

EmployeeDao のテストを行います。

package example.dao;

import org.seasar.extension.unit.S2TestCase;

import example.dao.impl.EmployeeDaoImpl;
import example.entity.Department;
import example.entity.Employee;

public class EmployeeDaoTest extends S2TestCase {

  private EmployeeDao dao;

  @Override
  protected void setUp() throws Exception {
    super.setUp();
    include("jpa.dicon");
  }

  public void testFindByIdTx() {
    Employee employee = dao.findById(1);
    assertNotNull(employee);
    assertEquals(new Integer(1), employee.getId());
    assertEquals("simagoro", employee.getName());

    Department department = employee.getDepartment();
    assertNotNull(department);
    assertEquals(new Integer(1), employee.getId());
    assertEquals("Account", department.getName());
    assertEquals(2, department.getEmployees().size());
  }
}

DAOから値が正しく取得できて、エンティティの関連もたどれます。

テスト実行時のログ出力

上記のテストを実行すると次のようなログが出力されます。

DEBUG 2007-01-15 03:35:01,223 [main] 永続ユニット(persistenceUnit)の永続クラス(example.entity.Department)が登録されます
DEBUG 2007-01-15 03:35:01,254 [main] 永続ユニット(persistenceUnit)の永続クラス(example.entity.Employee)が登録されます
DEBUG 2007-01-15 03:35:01,567 [main] 物理的なコネクションを取得しました
DEBUG 2007-01-15 03:35:01,567 [main] 論理的なコネクションを取得しました
DEBUG 2007-01-15 03:35:01,645 [main] 論理的なコネクションを閉じました
DEBUG 2007-01-15 03:35:01,989 [main] クラス(example.dao.impl.EmployeeDaoImpl[employeeDao])のコンポーネント定義を登録します
DEBUG 2007-01-15 03:35:01,989 [main] トランザクションを開始しました
DEBUG 2007-01-15 03:35:02,004 [main] 論理的なコネクションを取得しました
DEBUG 2007-01-15 03:35:02,020 [main] 論理的なコネクションを閉じました
DEBUG 2007-01-15 03:35:02,035 [main] 論理的なコネクションを取得しました
DEBUG 2007-01-15 03:35:02,035 [main] 論理的なコネクションを閉じました
DEBUG 2007-01-15 03:35:02,035 [main] トランザクションロールバックしました
DEBUG 2007-01-15 03:35:02,035 [main] 物理的なコネクションを閉じました

最初の2行はエンティティクラスが自動検出され永続ユニットに登録されたことを示しています。


以上でサンプルは終わりです。