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を上書きするか聞かれるので、すべてはいのボタンをクリックして、すべてを取り込みます。 これで、私と全く同じ環境になります。

サンプルはS2HibernateExamleVx.x.x.jarとして別途用意されているので、ダウンロードして解凍してください。 Eclipseでs2hibernate-exampleという名前でJavaプロジェクトを作成します。 解凍してできたs2hibernate-exampleディレクトリを丸ごとインポートしてください。 .classpathを上書きするか聞かれるので、すべてはいのボタンをクリックして、すべてを取り込みます。

コンソールアプリサンプル

WEB-INF/src/examples配下にJavaアプリケーションのサンプルがあります。
S2Hibernateとして必要なjarファイルは、s2hibernate/WEB-INF/libにそろってます。 Hibernateはコネクションプールやキャッシュの実装をいろいろ選べるようになっているので、 S2Hibernateで用意していない実装が必要な場合は、Hibernateのサイトよりダウンロードしてください。

簡単に機能を試すことができるように、RDBMSとしてHSQLDBを用意しています。 機能を試す前にあらかじめHSQLDBを実行しておいてください。 HSQLDBを実行するには、WEB-INF/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にインポートして使う場合は設定は不要です。

OpenSessionInViewサンプル

Tomcatと Tomcat Pluginを使うことを前提にしています。 あらかじめインストールして置いてください。 s2hibernate-exampleプロジェクトを右クリックしてプロパティ->Tomcatを選びます。 Tomcatプロジェクトであるをチェックし、アプリケーションURIを/s2hibernate-exampleとします。 これで、Tomcatを再起動して、ブラウザからhttp://localhost:8080/s2hibernate-example/にアクセスするとOpenSessionInViewサンプルを見ることができます。

使い方

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のおいしいところだけを簡単に利用できるということが分かっていただけたと思います。


Open Session In View

"Open Session In View"パターンは、ビューでSessionをオープンする機能です。実際には、クライアントから来たリクエストがServletFilterを通るときに自動的にSessionをオープンしRequestオブジェクトに設定、逆にリクエストがServletFilterを通ってかえるときに、Sessionを自動的に閉じます。 この機能により、JSPの中でSessionをすぐに使うことができます。もちろん、Sessionのオープン、クローズは、OpenSessionInViewFilterによって自動的に処理されるので、記述する必要はありません。

セットアップ

次の2つのファイルに設定を追加します。
・web.xmlに、OpenSessionInViewFilterの設定を追加。
・app.diconに、OpenSessionInView.diconを読み込む設定を追加。

web.xml

    <filter>
<filter-name>OpenSessionInView</filter-name>
<filter-class>org.seasar.hibernate.filter.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInView</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

app.dicon

    <include path="org/seasar/hibernate/filter/OpenSessionInView.dicon"/>

Example

S2Session.jsp

フィルターで自動設定されたS2Sessionを直接使うサンプル
<%@page contentType="text/html"%>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core"%>
<%@page import="java.util.*"%>
<%@page import="org.seasar.hibernate.*"%>
<%
S2Session s2session = ((S2Session)request.getAttribute("S2Session"));
List employeeList = s2session.find("from Employee");
request.setAttribute("employeeList",employeeList);
%>

<html>
<head><title>Employee List</title></head>
<body>

<h2>Employee List</h2>

<table>
<c:forEach items="${employeeList}"var="employee">
<tr>
<td><c:out value="${employee.empno}"/></td>
<td><c:out value="${employee.ename}"/></tr>
</c:forEach>
</table>
</body>
</html>

departmentDao.jsp

JSPで、Service層を呼び出すサンプル(遅延ロード)
<%@page contentType="text/html"%>
<%@taglib prefix="c" uri="http://java.sun.com/jstl/core"%>
<%@page import="java.util.*"%>
<%@page import="org.seasar.hibernate.*"%>
<%@page import="org.seasar.framework.container.S2Container"%>
<%@page import="org.seasar.framework.container.factory.SingletonS2ContainerFactory"%>
<%@page import="examples.hibernate.service.DepartmentService"%>
<%
S2Container container = SingletonS2ContainerFactory.getContainer();
DepartmentService service = (DepartmentService) container.getComponent(DepartmentService.class);
request.setAttribute("department",service.getDepartment((short)20));
System.out.println("== html start ==");
%>

<html>
<head><title>Department</title></head>
<body>

<h2>Department</h2>
<c:out value="${department.deptno}"/><br>
<c:out value="${department.dname}"/><br>
<h2>EmployeeList</h2>
<table>
<c:forEach items="${department.employeeList}"var="e">
<tr>
<td><c:out value="${e.empno}"/></td>
<td><c:out value="${e.ename}"/></td>
</tr>
</c:forEach>
</table>
<%
System.out.println("== html end ==");
%>
</body>

</html>
実行結果
DEBUG 2004-07-24 12:16:04,260 [http-8080-Processor24] トランザクションを開始しました
DEBUG 2004-07-24 12:16:04,260 [http-8080-Processor24] 論理的なコネクションを取得しました
DEBUG 2004-07-24 12:16:05,412 [http-8080-Processor24] 論理的なコネクションを取得しました
Hibernate: select department0_.DEPTNO as DEPTNO, department0_.DNAME as DNAME, department0_.LOC as LOC 
    from DEPT department0_ where (deptno=? )
== html start ==
Hibernate: select employeeli0_.EMPNO as EMPNO__, employeeli0_.DEPTNO as DEPTNO__, 
	employeeli0_.EMPNO as EMPNO0_, employeeli0_.ENAME as ENAME0_, employeeli0_.JOB as JOB0_,
	employeeli0_.MGR as MGR0_, employeeli0_.HIREDATE as HIREDATE0_,	employeeli0_.SAL as SAL0_, 
	employeeli0_.COMM as COMM0_, employeeli0_.DEPTNO as DEPTNO0_ 
	from EMP employeeli0_ where employeeli0_.DEPTNO=?
== html end ==
DEBUG 2004-07-24 12:16:05,692 [http-8080-Processor24] 論理的なコネクションを閉じました
DEBUG 2004-07-24 12:16:05,762 [http-8080-Processor24] トランザクションをコミットしました
Service層の外でも、遅延ロードが設定されているオブジェクトの情報が取得できることを確認していただけると思います。