A Day In The Life

とあるプログラマの備忘録

SpringとHibernateと時々Generics。

HibernateとSpringの連携を使って汎用Daoを作ってみます。

Daoって同じようなコードであふれることがよくあるのでGenericsを使って整理するのがポイントです。

まずModelクラスを実装します。

public class Product implements Serializable {
    private Long id;
    private String name;
    private BigDecimal unitPrice;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public BigDecimal getUnitPrice() {
        return unitPrice;
    }
    public void setUnitPrice(BigDecimal unitPrice) {
        this.unitPrice = unitPrice;
    }
    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Product)) {
            return false;
        }
        return this.id.compareTo(((Product) o).id) == 0;
    }
    @Override
    public int hashCode() {
        return id == null ? 1 : id.hashCode();
    }
}

なんの変哲もないただのBeanです。

DBのテーブルはこんな感じです。

create table PRODUCT (
	ID numeric(13) primary key,
	NAME varchar(40) not null,
	UNITPRICE numeric(13,2) not null
)

モデルと1対1のテーブルです。

このテーブルにアクセスするDaoを普通に作るとたぶんこんな感じになるかと思います。

import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class ProductDao extends HibernateDaoSupport {
    @SuppressWarnings("unchecked")
    public List<Product> findByName(String name) {
        return getHibernateTemplate().find(
            "from Product as p where p.name like ?", "%" + name + "%");
    }
    @SuppressWarnings("unchecked")
    public Product find(Long id) {
        return (Product) getHibernateTemplate().get(Product.class, id);
    }
    public void add(Product product) {
        getHibernateTemplate().save(product);
    }
    public void update(Product product) {
        getHibernateTemplate().update(product);
    }
    public void delete(Product product) {
        getHibernateTemplate().delete(product);
    }
}

find,add,update,deleteメソッドはたぶん他のテーブルでも同じような実装になるはずですよね。

こんなの毎回コピペしてクラス名だけ書き替えるのはめんどくさいし楽しくない…。

そこでGenericsを使って基底クラスを作り毎回find,add,update,deleteメソッドを書かなくていいようにしてみましょう。

import java.io.Serializable;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class BaseDao<G, PK extends Serializable> extends HibernateDaoSupport {
    private Class<G> type;
    public BaseDao(Class<G> type) {
        this.type = type;
    }
    @SuppressWarnings("unchecked")
    public G find(PK id) {
        return (G) getHibernateTemplate().get(type, id);
    }
    public void add(G entity) {
        getHibernateTemplate().save(entity);
    }
    public void update(G entity) {
        getHibernateTemplate().update(entity);
    }
    public void delete(G entity) {
        getHibernateTemplate().delete(entity);
    }
    public void updateAll(List<G> entities) {
        for(G entity : entities) {
            getHibernateTemplate().update(entity);
        }
    }
    public void saveOrUpdateAll(List<G> entities) {
        getHibernateTemplate().saveOrUpdateAll(entities);
    }
    public void deleteAll(List<G> entities) {
        getHibernateTemplate().deleteAll(entities);    
    }
}

基本的な処理は一通り基底クラスでできるようにしておきます。

本当はfindメソッド

return (G) getHibernateTemplate().get(G.class, id);

とできるといいのですがJavaではコンパイルエラーになってしまいます。
でサブクラスの実装はというと

import java.util.List;

public class ProductDao extends BaseDao<Product, Long> {
    public ProductDao() {    
        super(Product.class);
    }
    @SuppressWarnings("unchecked")
    public List<Product> findByName(String name) {
        return getHibernateTemplate().find(
            "from Product as p where p.name like ?", "%" + name + "%");
    }
    :
    その他、Product独自のfindメソッドをここに書く
    :
}

こんな感じになります。

基底クラスを作成することにより、サブクラスは独自の検索メソッドに専念できます。

この後、Springの流儀的にはProductDaoのInterfaceを作ってサービスでインジェクションするのがベストなんですがそのあたりは割愛します。

SpringのHibernateTemplateを使うだけでもだいぶコードは減りますがGenericsを使った基底クラスを作ることでさらにコードが減らせます。