読者です 読者をやめる 読者になる 読者になる

GenericDaoについて記しておくか・

Java Hibernate Spring

いちをう、

GenericDaoとは

DAOのスーパークラスジェネリクスで汎用的なCRUDを定義しておき、個々のDAOにタイプセーフなCRUD操作を提供するパターン。

GenericDaoインターフェースとImpl

GenericsでCRUD操作の定義

package etc9.dao;

import java.io.Serializable;

public interface GenericDao<T, PK extends Serializable> {
    public T get(PK id);
    public PK sava(T entity);
    public void update(T entity);
    public void delete(T entity);
}

GenericDaoの実装。実際の処理は hibernateTemplate に委譲。typeフィールドには、ごにゃごにゃやって、T 型のクラスを格納。

package etc9.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;

public class GenericDaoImpl <T, PK extends Serializable>
    implements GenericDao<T, PK> {

    @SuppressWarnings("unchecked")
    private final Class<T> type = (Class<T>)((ParameterizedType)getClass()
            .getGenericSuperclass()).getActualTypeArguments()[0];

    protected Class<T> getType() {
        return type;
    }

    private HibernateTemplate hibernateTemplate;

    @Autowired(required=true)
    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }
    public HibernateTemplate getHibernateTemplate() {
        return hibernateTemplate;
    }

    @Override @SuppressWarnings("unchecked")
    public T get(PK id) {
        return (T) hibernateTemplate.get(getType(), id);
    }

    @Override @SuppressWarnings("unchecked")
    public PK sava(T entity) {
        return (PK) hibernateTemplate.save(entity);
    }

    @Override 
    public void update(T entity) {
        hibernateTemplate.update(entity);
    }
    
    @Override 
    public void delete(T entity) {
        hibernateTemplate.delete(entity);
    }
}

CustomerDaoと実装クラス

package etc9.dao;

import etc9.domain.Customer;

public interface CustomerDao extends GenericDao<Customer, Long> {
}
package etc9.dao;

import org.springframework.stereotype.Repository;

import etc9.domain.Customer;

@Repository
public class CustomerDaoImpl extends GenericDaoImpl<Customer, Long>
        implements CustomerDao {
}

以上ー

顧客エンティティ

では、永続化対象のCustomer

package etc9.domain;

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

@Entity
public class Customer {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String address;
    
    public Customer() {}
    public Customer(String name, String address) {
        this.name = name;
        this.address = address;
    }
    ・・・以下アクセッサー
}

applicationContext.xml

勝手にspringにスキャンさせまっす

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">

  <context:component-scan base-package="etc9.dao"/>
  
  <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.h2.Driver"/>
    <property name="url" value="jdbc:h2:tcp://localhost:9092/spring"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>
 
  <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect = org.hibernate.dialect.H2Dialect
        hibernate.show_sql = true
        hibernate.hbm2ddl.auto = create
      </value>
    </property>
    <property name="annotatedClasses">
     <list><value>etc9.domain.Customer</value></list>
    </property>
  </bean>

</beans>

テストケース

動作確認だけ・・

package etc9.dao;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import etc9.domain.Customer;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class CustomerDaoTest {

    @Autowired
    private CustomerDao dao;
    private static org.h2.tools.Server tcpServer;

    @BeforeClass
    public static void beforeClass() throws Exception {
        tcpServer = org.h2.tools.Server.createTcpServer(
                new String[] { "-baseDir", "db\\h2", "-tcpPort", "9092" }).start();
    }
    @AfterClass
    public static void afterClass() throws Exception {
        tcpServer.shutdown();
    }

    @Test public void sava() throws Exception {
        
        Customer c1 = new Customer("etc9", "Tokyo");
        assertThat(dao.sava(c1), is(1L));
        
        c1 = dao.get(1L);
        assertThat(c1.getName(), is("etc9"));

        c1.setAddress("NY");
        dao.update(c1);
        dao.delete(c1);
    }
}

こんなSQL吐きよった

Hibernate: insert into Customer (id, address, name) values (null, ?, ?)
Hibernate: select customer0_.id as id0_0_, customer0_.address as address0_0_, customer0_.name as name0_0_ from Customer customer0_ where customer0_.id=?
Hibernate: update Customer set address=?, name=? where id=?
Hibernate: delete from Customer where id=?

ちなみに、SpringJUnit4ClassRunner とか使ってるので、「spring-test.jar」をクラスパスに追加する必要がある。
あと、Hibernateアノテーション使うので、「ejb3-persistence.jar、hibernate-annotations.jar、hibernate-commons-annotations.jar」もクラスパスに追加する。その他こまごましたのは省略。