Seasar DI Container with AOP

S2Hibernateを使うと、S2のJTAやConnectionPoolとHibernateが簡単に連動するようになります。これまでHibernateで開発するときの悩みの種だったSession管理をS2Hibernateが自動的に行ってくれるので、開発者はSessionのオープン・クローズ、Transaction処理から開放されます。SessionはJTAのトランザクション中は維持され、トランザクションの終了時に自動的にクローズ(flushも)されます。

セットアップ

S2と同様にJDK1.4以上が必要です。S2HibernateVx.x.x.jarを解凍してできたs2hibernateディレクトリをEclipseのJavaプロジェクトとして丸ごとインポートしてください。.classpathを上書きするか聞かれるので、すべてはいのボタンをクリックして、すべてを取り込みます。 これで、私と全く同じ環境になります。src/examples配下にサンプルもあります。

S2Hibernateとして必要なjarファイルは、s2hibernate/libにそろってます。Hibernateはコネクションプールやキャッシュの実装をいろいろ選べるようになっているので、S2Hibernateで用意していない実装が必要な場合は、Hibernateのサイトよりダウンロードしてください。簡単に機能を試すことができるように、RDBMSとしてHSQLDBを用意しています。機能を試す前にあらかじめHSQLDBを実行しておいてください。HSQLDBを実行するには、bin/runHsqldb.batをダブルクリック(Windowsの場合)します。lib/hsqldb.jarはHSQLDBを実行する上では必要ですが、本番では必要ありません。libのjarファイル(hsqldb.jar以外)とsrcのhibernate.cfg.xml、ehcache.xml、j2ee.dicon、log4j.propertiesをCLASSPATHにとおせば、S2Hibernateを実行できます。Eclipseにインポートして使う場合は設定は不要です。

使い方

Hibernateを使いたいクラスは、S2SessionFactory型のフィールドを定義してコンストラクタあるいはプロパティ経由で実装クラス(S2SessionFactoryImpl)を受け取ります。あとは、S2SessionFactory.getSession()を呼び出してHibernateの機能を自由に使うことができます。Sessionのオープン・クローズ、トランザクション制御は一切必要ありません。S2SessionFactory.getSession()が返すオブジェクトは、HibernateのSessionのラッパーであるS2Sessionです。実行できるメソッドはHibernateのSessionと同じで、検査例外(HibernateException)を実行時例外(HibernateRuntimeException)に変換してくれます。そのため、HibernateExceptionをcatchしたくないクラスは特に何もする必要はありません。catchする必要がある場合は、HibernateRuntimeExceptionでcatchしてgetCause()でHibernateExceptionを取り出します。

Example

Employee.java

package examples.hibernate.entity;

import java.io.Serializable;

public class Employee implements Serializable {

    private long empno;

    private String ename;

    private String job;

    private Short mgr;

    private java.util.Date hiredate;

    private Float sal;

    private Float comm;

    private short deptno;

    public Employee(long empno, java.lang.String ename, java.lang.String job, Short mgr,
	  java.util.Date hiredate, Float sal, Float comm, short deptno) {
        this.empno = empno;
        this.ename = ename;
        this.job = job;
        this.mgr = mgr;
        this.hiredate = hiredate;
        this.sal = sal;
        this.comm = comm;
        this.deptno = deptno;
    }

    public Employee() {
    }

    public Employee(long empno) {
        this.empno = empno;
    }

    public long getEmpno() {
        return this.empno;
    }

    public void setEmpno(long empno) {
        this.empno = empno;
    }

    public java.lang.String getEname() {
        return this.ename;
    }

    public void setEname(java.lang.String ename) {
        this.ename = ename;
    }

    public java.lang.String getJob() {
        return this.job;
    }

    public void setJob(java.lang.String job) {
        this.job = job;
    }

    public Short getMgr() {
        return this.mgr;
    }

    public void setMgr(Short mgr) {
        this.mgr = mgr;
    }

    public java.util.Date getHiredate() {
        return this.hiredate;
    }

    public void setHiredate(java.util.Date hiredate) {
        this.hiredate = hiredate;
    }

    public Float getSal() {
        return this.sal;
    }

    public void setSal(Float sal) {
        this.sal = sal;
    }

    public Float getComm() {
        return this.comm;
    }

    public void setComm(Float comm) {
        this.comm = comm;
    }

    public short getDeptno() {
        return this.deptno;
    }

    public void setDeptno(short deptno) {
        this.deptno = deptno;
    }

    public boolean equals(Object other) {
        if ( !(other instanceof Employee) ) return false;
        Employee castOther = (Employee) other;
        return this.getEmpno() == castOther.getEmpno();
    }

    public int hashCode() {
        return (int) this.getEmpno();
    }
}

Employee.hbm.xml

<hibernate-mapping>
<class name="examples.hibernate.entity.Employee" table="EMP">
<id name="empno" column="EMPNO" type="long">
<generator class="assigned"/>
</id>
<property name="ename" column="ENAME" type="string" length="10"/>
<property name="job" column="JOB" type="string" length="9"/>
<property name="mgr" column="MGR" type="short" length="4"/>
<property name="hiredate" column="HIREDATE" type="timestamp"/>
<property name="sal" column="SAL" type="float" length="7"/>
<property name="comm" column="COMM" type="float" length="7"/>
<property name="deptno" column="DEPTNO" type="short" length="2"/>
</class>
</hibernate-mapping>

EmployeeDao.java

package examples.hibernate.dao;

import examples.hibernate.entity.Employee;

public interface EmployeeDao {

    public Employee getEmployee(int empno);
}

EmployeeDaoImpl.java

package examples.hibernate.dao;

import java.util.List;

import net.sf.hibernate.Hibernate;

import org.seasar.hibernate.S2SessionFactory;

import examples.hibernate.entity.Employee;

public class EmployeeDaoImpl implements EmployeeDao {

    private static final String HQL = "from Employee where empno = ?";
    private S2SessionFactory sessionFactory_;

    public EmployeeDaoImpl(S2SessionFactory sessionFactory) {
        sessionFactory_ = sessionFactory;
    }

    public Employee getEmployee(int empno) {
        List result = sessionFactory_.getSession().find(
            HQL, new Integer(empno), Hibernate.INTEGER);
        if (result.size() > 0) {
            return (Employee) result.get(0);
        } else {
            return null;
        }
    }
}

EmployeeService.java

package examples.hibernate.service;

import examples.hibernate.entity.Employee;

public interface EmployeeService {

    public Employee getEmployee(int empno);
}

EmployeeServiceImpl.java

package examples.hibernate.service;

import examples.hibernate.dao.EmployeeDao;
import examples.hibernate.entity.Employee;

public class EmployeeServiceImpl implements EmployeeService {

    private EmployeeDao employeeDao_;

    public EmployeeServiceImpl(EmployeeDao employeeDao) {
        employeeDao_ = employeeDao;
    }

    public Employee getEmployee(int empno) {
        return employeeDao_.getEmployee(empno);
    }
}

Employee.dicon

<components>
<include path="j2ee.dicon"/>
<component class="org.seasar.hibernate.impl.S2SessionFactoryImpl"/>
<component class="examples.hibernate.dao.EmployeeDaoImpl"/>
<component class="examples.hibernate.service.EmployeeServiceImpl">
<aspect>j2ee.requiredTx</aspect>
</component>
</components>

EmployeeClient.java

package examples.hibernate.client;

import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;

import examples.hibernate.service.EmployeeService;

public class EmployeeClient {

    private static final String PATH =
        "examples/hibernate/client/Employee.dicon";

    public static void main(String[] args) {
        S2Container container = S2ContainerFactory.create(PATH);
        container.init();
        try {
            EmployeeService service =
                (EmployeeService) container.getComponent(EmployeeService.class);
            System.out.println(service.getEmployee(7788).getEname());
        } finally {
            container.destroy();
        }

    }
}

実行結果

DEBUG 2004-04-07 18:53:52,511 [main] トランザクションを開始しました
2004/04/07 18:53:52 net.sf.hibernate.cfg.Environment <clinit>
情報: Hibernate 2.1.2
省略 DEBUG 2004-04-07 18:53:54,654 [main] 物理的なコネクションを取得しました
DEBUG 2004-04-07 18:53:54,744 [main] 論理的なコネクションを取得しました
Hibernate: select employee0_.EMPNO as EMPNO, employee0_.ENAME as ENAME, employee0_.JOB as JOB, employee0_.MGR as MGR, employee0_.HIREDATE as HIREDATE, employee0_.SAL as SAL, employee0_.COMM as COMM, employee0_.DEPTNO as DEPTNO from EMP employee0_ where (empno=? )
DEBUG 2004-04-07 18:53:55,605 [main] 論理的なコネクションを閉じました
DEBUG 2004-04-07 18:53:55,625 [main] トランザクションをコミットしました
SCOTT
DEBUG 2004-04-07 18:53:55,625 [main] 物理的なコネクションを閉じました
S2Hibernateを使えば、Hibernateのおいしいところだけを簡単に利用できるということが分かっていただけたと思います。