首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Generic DAO 模式:基於 Java 泛型與 Hibernate 的通用數據訪問層設計

     編輯:Java 程式設計入門

動機

傳統 DAO 為每個實體類編寫重複的 CRUD 代碼 (save、delete、findById、findAll…),違反 DRY 原則。Hibernate 官方推薦的 Generic DAO 模式利用 JDK 5 泛型,將通用操作提取到基類,具體 DAO 僅需關注特有查詢。

核心接口:IGenericDAO

public interface IGenericDAO {

    T findById(ID id);
    T findById(ID id, boolean lock); // 支持悲觀鎖
    List findAll();
    List findByExample(T exampleInstance);
    List findByExample(T exampleInstance, String[] excludeProperties);
    T makePersistent(T entity);      // saveOrUpdate
    void makeTransient(T entity);    // delete
}

Hibernate 實現:GenericDAOHibernate

public abstract class GenericDAOHibernate> 
        implements IGenericDAO {

    private Class persistentClass;
    protected Session session;

    @SuppressWarnings("unchecked")
    public GenericDAOHibernate() {
        // 通過反射獲取泛型實際類型參數
        this.persistentClass = (Class) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public DAOImpl setSession(Session s) {
        this.session = s;
        return (DAOImpl) this; // 支持鏈式調用
    }

    protected Session getSession() {
        if (session == null) throw new IllegalStateException("Session 未設置");
        return session;
    }

    // ---------- 通用 CRUD 實現 ----------
    @Override @SuppressWarnings("unchecked")
    public T findById(ID id) {
        return (T) getSession().load(getPersistentClass(), id);
    }

    @Override @SuppressWarnings("unchecked")
    public T findById(ID id, boolean lock) {
        if (lock) return (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
        return findById(id);
    }

    @Override @SuppressWarnings("unchecked")
    public List findAll() {
        return findByCriteria();
    }

    @Override @SuppressWarnings("unchecked")
    public List findByExample(T exampleInstance) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        crit.add(Example.create(exampleInstance));
        return crit.list();
    }

    @Override @SuppressWarnings("unchecked")
    public List findByExample(T exampleInstance, String[] excludeProperties) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        Example example = Example.create(exampleInstance);
        for (String prop : excludeProperties) example.excludeProperty(prop);
        crit.add(example);
        return crit.list();
    }

    @Override @SuppressWarnings("unchecked")
    public T makePersistent(T entity) {
        getSession().saveOrUpdate(entity);
        return entity;
    }

    @Override
    public void makeTransient(T entity) {
        getSession().delete(entity);
    }

    // ---------- 靈活的 Criteria 封裝 ----------
    @SuppressWarnings("unchecked")
    protected List findByCriteria(Criterion... criterions) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterions) crit.add(c);
        return crit.list();
    }

    @SuppressWarnings("unchecked")
    protected List findByCriteria(Order order, Criterion... criterions) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterions) crit.add(c);
        if (order != null) crit.addOrder(order);
        return crit.list();
    }

    @SuppressWarnings("unchecked")
    protected List findByCriteria(int firstResult, int maxResults, Order order, Criterion... criterions) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterions) crit.add(c);
        if (order != null) crit.addOrder(order);
        crit.setFirstResult(firstResult);
        crit.setMaxResults(maxResults);
        return crit.list();
    }

    public Class getPersistentClass() { return persistentClass; }
}

具體 DAO 繼承示例

// 1. 定義擴展接口
public interface IUserDAO extends IGenericDAO {
    User findByUsername(String username);
    User findByUsernameAndPassword(String username, String password);
}

// 2. 實現類繼承基類
public class UserDAOHibernate extends GenericDAOHibernate implements IUserDAO {

    @Override
    public User findByUsername(String username) {
        return findByCriteria(Restrictions.eq("username", username)).stream().findFirst().orElse(null);
    }

    @Override
    public User findByUsernameAndPassword(String username, String password) {
        return findByCriteria(
            Restrictions.eq("username", username),
            Restrictions.eq("password", password)
        ).stream().findFirst().orElse(null);
    }
}

Spring 整合配置

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">...</bean>

<bean id="userDAO" class="com.example.dao.UserDAOHibernate">
    <property name="session" ref="sessionFactory" /> <!-- 注入 Session 或用 SessionFactory.getCurrentSession() -->
</bean>

優缺點與演進

優點缺點/注意
消除 80% 樣板代碼 泛型擦除導致運行時需反射獲取實體類,微小性能損耗
統一異常處理、事務邊界 複雜查詢仍需在子類寫 Criteria/HQL/原生 SQL
易於單元測試 (Mock Session) Hibernate 5/6 API 變化需適配 (如 SessionFactory/Session 管理)
支持鎖、分頁、排序、Example 查詢 現代項目多改用 Spring Data JPA (Repository 接口自動實現)

現代替代:Spring Data JPA

public interface UserRepository extends JpaRepository {
    User findByUsername(String username);
    User findByUsernameAndPassword(String username, String password);
    // 無需實現類,Spring 自動代理實現
}

總結

Generic DAO 是 Hibernate 時代經典設計模式,展示了泛型、模板方法、反射、Criteria API的綜合運用。雖然 Spring Data JPA 已成為主流,但理解 Generic DAO 原理有助於閱讀舊代碼、自定義 Repository 基類、排查 ORM 底層行為。核心思想——將不變的 CRUD 抽象到基類,將變的業務查詢留給子類——永不過時。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved