0

0

Generic Data Access Objects [转]

php中文网

php中文网

发布时间:2016-06-07 15:44:13

|

1127人浏览过

|

来源于php中文网

原创

The DAO interfaces An implementation with Hibernate Preparing DAOs with factories Preparing DAOs with manual dependency injection Preparing DAOs with lookup Writing DAOs as managed EJB 3.0 components A Better typed Generic DAO? You decide!

 

千帆大模型平台
千帆大模型平台

面向企业开发者的一站式大模型开发及服务运行平台

下载

    • The DAO interfaces
    • An implementation with Hibernate
    • Preparing DAOs with factories
    • Preparing DAOs with manual dependency injection
    • Preparing DAOs with lookup
    • Writing DAOs as managed EJB 3.0 components
    • A Better typed Generic DAO? You decide!

 

 

This is a pattern for Data Access Objects with JDK 5.0, from the CaveatEmptor example application. It is also explained in the book Java Persistence with Hibernate. Two links you might find useful: Sessions and transactions and Open Session in View.

This time I based the DAO example on interfaces. Tools like Hibernate already provide database portability, so persistence layer portability shouldn't be a driving motivation for interfaces. However, DAO interfaces make sense in more complex applications, when several persistence services are encapsulate in one persistence layer. I'd say that you should use Hibernate (or Java Persistence APIs) directly in most cases, the best reason to use an additional DAO layer is higher abstraction (e.g. methods like getMaximumBid()instead of session.createQuery(...) repeated a dozen times).

 

The DAO interfaces

I use one interface per persistent entity, with a super interface for common CRUD functionality:

public interface GenericDAO<T, ID extends Serializable> {
 
    T findById(ID id, boolean lock);
 
    List<T> findAll();
 
    List<T> findByExample(T exampleInstance);
 
    T makePersistent(T entity);
 
    void makeTransient(T entity);
}

 

You can already see that this is going to be a pattern for a state-oriented data access API, with methods such as makePersistent() and makeTransient(). Furthermore, to implement a DAO you have to provide a type and an identifier argument. As for most ORM solutions, identifier types have to be serializable.

The DAO interface for a particular entity extends the generic interface and provides the type arguments:

public interface ItemDAO extends GenericDAO<Item, Long> {
 
    public static final String QUERY_MAXBID = "ItemDAO.QUERY_MAXBID";
    public static final String QUERY_MINBID = "ItemDAO.QUERY_MINBID";
 
    Bid getMaxBid(Long itemId);
    Bid getMinBid(Long itemId);
 
}

 

We basically separate generic CRUD operations and actual business-related data access operations from each other. (Ignore the named query constants for now, they are convenient if you use annotations.) However, even if only CRUD operations are needed for a particular entity, you should still write an interface for it, even it it is going to be empty. It is important to use a concrete DAO in your controller code, otherwise you will face some refactoring once you have to introduce specific data access operations for this entity.

 

An implementation with Hibernate

An implementation of the interfaces could be done with any state-management capable persistence service. First, the generic CRUD implementation with Hibernate:

public abstract class GenericHibernateDAO<T, ID extends Serializable>
        implements GenericDAO<T, ID> {
 
    private Class<T> persistentClass;
    private Session session;
 
    public GenericHibernateDAO() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
     }
 
    @SuppressWarnings("unchecked")
    public void setSession(Session s) {
        this.session = s;
    }
 
    protected Session getSession() {
        if (session == null)
            throw new IllegalStateException("Session has not been set on DAO before usage");
        return session;
    }
 
    public Class<T> getPersistentClass() {
        return persistentClass;
    }
 
    @SuppressWarnings("unchecked")
    public T findById(ID id, boolean lock) {
        T entity;
        if (lock)
            entity = (T) getSession().load(getPersistentClass(), id, LockMode.UPGRADE);
        else
            entity = (T) getSession().load(getPersistentClass(), id);
 
        return entity;
    }
 
    @SuppressWarnings("unchecked")
    public List<T> findAll() {
        return findByCriteria();
    }
 
    @SuppressWarnings("unchecked")
    public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        Example example =  Example.create(exampleInstance);
        for (String exclude : excludeProperty) {
            example.excludeProperty(exclude);
        }
        crit.add(example);
        return crit.list();
    }
 
    @SuppressWarnings("unchecked")
    public T makePersistent(T entity) {
        getSession().saveOrUpdate(entity);
        return entity;
    }
 
    public void makeTransient(T entity) {
        getSession().delete(entity);
    }
 
    public void flush() {
        getSession().flush();
    }
 
    public void clear() {
        getSession().clear();
    }
 
    /**
     * Use this inside subclasses as a convenience method.
     */
    @SuppressWarnings("unchecked")
    protected List<T> findByCriteria(Criterion... criterion) {
        Criteria crit = getSession().createCriteria(getPersistentClass());
        for (Criterion c : criterion) {
            crit.add(c);
        }
        return crit.list();
   }
 
}

 

There are some interesting things in this implementation. First, it clearly needs a Session to work, provided with setter injection. You could also use constructor injection. How you set the Session and what scope this Session has is of no concern to the actual DAO implementation. A DAO should not control transactions or the Session scope.

 

We need to suppress a few compile-time warnings about unchecked casts, because Hibernate's interfaces are JDK 1.4 only. What follows are the implementations of the generic CRUD operations, quite straightforward. The last method is quite nice, using another JDK 5.0 feature, varargs. It helps us to build Criteria queries in concrete entity DAOs. This is an example of a concrete DAO that extends the generic DAO implementation for Hibernate:

public class ItemDAOHibernate
        extends     GenericHibernateDAO<Item, Long>
        implements  ItemDAO {
 
    public Bid getMaxBid(Long itemId) {
        Query q = getSession().getNamedQuery(ItemDAO.QUERY_MAXBID);
        q.setParameter("itemid", itemId);
        return (Bid) q.uniqueResult();
    }
 
    public Bid getMinBid(Long itemId) {
        Query q = getSession().getNamedQuery(ItemDAO.QUERY_MINBID);
        q.setParameter("itemid", itemId);
        return (Bid) q.uniqueResult();
    }
 
}

 

Another example which uses the findByCriteria() method of the superclass with variable arguments:

public class CategoryDAOHibernate
        extends     GenericHibernateDAO<Category, Long>
        implements  CategoryDAO {
 
    public Collection<Category> findAll(boolean onlyRootCategories) {
        if (onlyRootCategories)
            return findByCriteria( Expression.isNull("parent") );
        else
            return findAll();
    }
}

 

Preparing DAOs with factories

We could bring it all together in a DAO factory, which not only sets the Session when a DAO is constructed but also contains nested classes to implement CRUD-only DAOs with no business-related operations:

 

public class HibernateDAOFactory extends DAOFactory {
 
    public ItemDAO getItemDAO() {
        return (ItemDAO)instantiateDAO(ItemDAOHibernate.class);
    }
 
    public CategoryDAO getCategoryDAO() {
        return (CategoryDAO)instantiateDAO(CategoryDAOHibernate.class);
    }
 
    public CommentDAO getCommentDAO() {
        return (CommentDAO)instantiateDAO(CommentDAOHibernate.class);
    }
 
    public ShipmentDAO getShipmentDAO() {
        return (ShipmentDAO)instantiateDAO(ShipmentDAOHibernate.class);
    }
 
    private GenericHibernateDAO instantiateDAO(Class daoClass) {
        try {
            GenericHibernateDAO dao = (GenericHibernateDAO)daoClass.newInstance();
            dao.setSession(getCurrentSession());
            return dao;
        } catch (Exception ex) {
            throw new RuntimeException("Can not instantiate DAO: " + daoClass, ex);
        }
    }
 
    // You could override this if you don't want HibernateUtil for lookup
    protected Session getCurrentSession() {
        return HibernateUtil.getSessionFactory().getCurrentSession();
    }
 
    // Inline concrete DAO implementations with no business-related data access methods.
    // If we use public static nested classes, we can centralize all of them in one source file.
 
    public static class CommentDAOHibernate
            extends GenericHibernateDAO<Comment, Long>
            implements CommentDAO {}
 
    public static class ShipmentDAOHibernate
            extends GenericHibernateDAO<Shipment, Long>
            implements ShipmentDAO {}
 
}

 

This concrete factory for Hibernate DAOs extends the abstract factory, which is the interface we'll use in application code:

public abstract class DAOFactory {
 
    /**
     * Creates a standalone DAOFactory that returns unmanaged DAO
     * beans for use in any environment Hibernate has been configured
     * for. Uses HibernateUtil/SessionFactory and Hibernate context
     * propagation (CurrentSessionContext), thread-bound or transaction-bound,
     * and transaction scoped.
     */
    public static final Class HIBERNATE = org.hibernate.ce.auction.dao.hibernate.HibernateDAOFactory.class;
 
    /**
     * Factory method for instantiation of concrete factories.
     */
    public static DAOFactory instance(Class factory) {
        try {
            return (DAOFactory)factory.newInstance();
        } catch (Exception ex) {
            throw new RuntimeException("Couldn't create DAOFactory: " + factory);
        }
    }
 
    // Add your DAO interfaces here
    public abstract ItemDAO getItemDAO();
    public abstract CategoryDAO getCategoryDAO();
    public abstract CommentDAO getCommentDAO();
    public abstract ShipmentDAO getShipmentDAO();
 
}

 

Note that this factory example is suitable for persistence layers which are primarily implemented with a single persistence service, such as Hibernate or EJB 3.0 persistence. If you have to mix persistence APIs, for example, Hibernate and plain JDBC, the pattern changes slightly. Keep in mind that you can also call session.connection() inside a Hibernate-specific DAO, or use one of the many bulk operation/SQL support options in Hibernate 3.1 to avoid plain JDBC.

 

Finally, this is how data access now looks like in controller/command handler code (pick whatever transaction demarcation strategy you like, the DAO code doesn't change):

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {
 
    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();
 
    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();
 
    DAOFactory factory = DAOFactory.instance(DAOFactory.HIBERNATE);
    ItemDAO itemDAO = factory.getItemDAO();
    UserDAO userDAO = factory.getUserDAO();
 
    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);
 
    Item item = itemDAO.findById(itemId, true);
 
    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);
 
    // JTA: utx.commit(); // Don't forget exception handling
 
    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling
 
}

 

The database transaction, either JTA or direct JDBC, is started and committed in an interceptor that runs for every execute(), following the Open Session in View pattern. You can use AOP for this or any kind of interceptor that can be wrapped around a method call, see Session handling with AOP.

 

Preparing DAOs with manual dependency injection

You don't need to write the factories. You can as well just do this:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {
 
    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();
 
    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();
 
    ItemDAOHibernate itemDAO = new ItemDAOHibernate();
    itemDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());
 
    UserDAOHibernate userDAO = new UserDAOHibernate();
    userDAO.setSession(HibernateUtil.getSessionFactory().getCurrentSession());
 
    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);
 
    Item item = itemDAO.findById(itemId, true);
 
    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);
 
    // JTA: utx.commit(); // Don't forget exception handling
 
    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling
 
}

 

The disadvantage here is that the implementation classes (i.e. ItemDAOHibernate and UserDAOHibernate) of the persistence layer are exposed to the client, the controller. Also, constructor injection of the current Session might be more appropriate.

 

Preparing DAOs with lookup

Alternatively, call HibernateUtil.getSessionFactory().getCurrentSession() as a fallback, if the client didn't provide a Session when the DAO was constructed:

public abstract class GenericHibernateDAO<T, ID extends Serializable>
        implements GenericDAO<T, ID> {
 
    private Class<T> persistentClass;
    private Session session;
 
    public GenericHibernateDAO() {
        this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
   }
 
   public void setSession(Session session) {
        this.session = session;
   }
 
   protected void getSession() {
       if (session == null)
           session = HibernateUtil.getSessionFactory().getCurrentSession();
       return session;
   }
 
...

 

The controller now uses these stateless data access objects through direct instantiation:

// EJB3 CMT: @TransactionAttribute(TransactionAttributeType.REQUIRED)
public void execute() {
 
    // JTA: UserTransaction utx = jndiContext.lookup("UserTransaction");
    // JTA: utx.begin();
 
    // Plain JDBC: HibernateUtil.getCurrentSession().beginTransaction();
 
    ItemDAO itemDAO = new ItemDAOHibernate();
    UserDAO userDAO = new UserDAOHibernate();
 
    Bid currentMaxBid = itemDAO.getMaxBid(itemId);
    Bid currentMinBid = itemDAO.getMinBid(itemId);
 
    Item item = itemDAO.findById(itemId, true);
 
    newBid = item.placeBid(userDAO.findById(userId, false),
                            bidAmount,
                            currentMaxBid,
                            currentMinBid);
 
    // JTA: utx.commit(); // Don't forget exception handling
 
    // Plain JDBC: HibernateUtil.getCurrentSession().getTransaction().commit(); // Don't forget exception handling
 
}

 

The only disadvantage of this very simple strategy is that the implementation classes (i.e. ItemDAOHibernate and UserDAOHibernate) of the persistence layer are again exposed to the client, the controller. You can still supply a custom Session if needed (integration test, etc).

 

Each of these methods (factories, manual injection, lookup) for setting the current Session and creating a DAO instance has advantages and drawbacks, use whatever you feel most comfortable with.

Naturally, the cleanest way is managed components and EJB 3.0 session beans:

 

Writing DAOs as managed EJB 3.0 components

Turn your DAO superclass into a base class for stateless session beans (all your concrete DAOs are then stateless EJBs, they already have a business interface). This is basically a single annotation which you could even move into an XML deployment descriptor if you like. You can then use dependency injection and get the "current" persistence context provided by the container:

@Stateless
public abstract class GenericHibernateDAO<T, ID extends Serializable>
        implements GenericDAO<T, ID> {
 
    private Class<T> persistentClass;
    
    @PersistenceContext
    private EntityManager em;
 
    public GenericHibernateDAO() {
       setSession( (Session)em.getDelegate() );    
    }
 
...

 

You can then cast the delegate of an EntityManager to a Hibernate Session.

This only works if you use Hibernate as a Java Persistence provider, because the delegate is the Session API. In JBoss AS you could even get a Session injected directly. If you use a different Java Persistence provider, rely on the EntityManager API instead of Session. Now wire your DAOs into the controller, which is also a managed component:

@Stateless
public class ManageAuctionController implements ManageAuction {
 
    @EJB ItemDAO itemDAO;
    @EJB UserDAO userDAO;
 
    @TransactionAttribute(TransactionAttributeType.REQUIRED) // This is even the default
    public void execute() {
 
        Bid currentMaxBid = itemDAO.getMaxBid(itemId);
        Bid currentMinBid = itemDAO.getMinBid(itemId);
 
        Item item = itemDAO.findById(itemId, true);
 
        newBid = item.placeBid(userDAO.findById(userId, false),
                               bidAmount,
                               currentMaxBid,
                               currentMinBid);
 
    }
}

 

P.S. Credit has to be given to Eric Burke, who first posted the basics for this pattern on his blog. Unfortunately, not even the Google cache is available anymore.

 

A Better typed Generic DAO? You decide!

We are missing something on end, since T allows you to "domain-ify" everything! and the Identifier type should ideally match the identifier type of T, but there's no way to do that on the code above. You decide which approach is better.

// Our common Model interface that an abstract Domain model will implement and all domain // models will extend.
public interface IModel<ID extends Serializable> {
        public abstract ID getId();
        public abstract void setId(final ID pId);
}
// Our generic DAO, NOTE: MODEL's ID type is the same as ID now, which makes sense.
// Also model type is more restrictive, dis-allowing all kinds of funky stuff to go in.
public abstract class GenericHibernateDAO<MODEL extends IModel<ID>, ID extends Serializable> implements GenericDAO<T, ID> {
 
    private Class<MODEL> persistentClass;
    private Session session;
 
    public GenericHibernateDAO() {
        // FIXME : I don't like magic number in the code, is there any way to fix 0 to something dynamic?
        this.persistentClass = (Class<MODEL>) ((ParameterizedType) getClass()
                                .getGenericSuperclass()).getActualTypeArguments()[0];
   }
 
   public final void setSession(final Session pSession) {
        this.session = session;
   }
 
   protected void getSession() {
       if (session == null)
           session = HibernateUtil.getSessionFactory().getCurrentSession();
       return session;
   }
...

 

In addition, we could add things like:

public final String getRootAlias(){
 this.getPersistentClass().getSimpleName() + String.valueOf('_');
}

 

Alhough this is not necessary or part of the enhanced version, but when criteria API is in use, this comes in handy.

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

705

2026.02.13

微博网页版主页入口与登录指南_官方网页端快速访问方法
微博网页版主页入口与登录指南_官方网页端快速访问方法

本专题系统整理微博网页版官方入口及网页端登录方式,涵盖首页直达地址、账号登录流程与常见访问问题说明,帮助用户快速找到微博官网主页,实现便捷、安全的网页端登录与内容浏览体验。

233

2026.02.13

Flutter跨平台开发与状态管理实战
Flutter跨平台开发与状态管理实战

本专题围绕Flutter框架展开,系统讲解跨平台UI构建原理与状态管理方案。内容涵盖Widget生命周期、路由管理、Provider与Bloc状态管理模式、网络请求封装及性能优化技巧。通过实战项目演示,帮助开发者构建流畅、可维护的跨平台移动应用。

117

2026.02.13

TypeScript工程化开发与Vite构建优化实践
TypeScript工程化开发与Vite构建优化实践

本专题面向前端开发者,深入讲解 TypeScript 类型系统与大型项目结构设计方法,并结合 Vite 构建工具优化前端工程化流程。内容包括模块化设计、类型声明管理、代码分割、热更新原理以及构建性能调优。通过完整项目示例,帮助开发者提升代码可维护性与开发效率。

22

2026.02.13

Redis高可用架构与分布式缓存实战
Redis高可用架构与分布式缓存实战

本专题围绕 Redis 在高并发系统中的应用展开,系统讲解主从复制、哨兵机制、Cluster 集群模式及数据分片原理。内容涵盖缓存穿透与雪崩解决方案、分布式锁实现、热点数据优化及持久化策略。通过真实业务场景演示,帮助开发者构建高可用、可扩展的分布式缓存系统。

61

2026.02.13

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

30

2026.02.12

雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法
雨课堂网页版登录入口与使用指南_官方在线教学平台访问方法

本专题系统整理雨课堂网页版官方入口及在线登录方式,涵盖账号登录流程、官方直连入口及平台访问方法说明,帮助师生用户快速进入雨课堂在线教学平台,实现便捷、高效的课程学习与教学管理体验。

15

2026.02.12

豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法
豆包AI网页版入口与智能创作指南_官方在线写作与图片生成使用方法

本专题汇总豆包AI官方网页版入口及在线使用方式,涵盖智能写作工具、图片生成体验入口和官网登录方法,帮助用户快速直达豆包AI平台,高效完成文本创作与AI生图任务,实现便捷智能创作体验。

669

2026.02.12

PostgreSQL性能优化与索引调优实战
PostgreSQL性能优化与索引调优实战

本专题面向后端开发与数据库工程师,深入讲解 PostgreSQL 查询优化原理与索引机制。内容包括执行计划分析、常见索引类型对比、慢查询优化策略、事务隔离级别以及高并发场景下的性能调优技巧。通过实战案例解析,帮助开发者提升数据库响应速度与系统稳定性。

58

2026.02.12

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Pandas 教程
Pandas 教程

共15课时 | 1.1万人学习

SQL 教程
SQL 教程

共61课时 | 4万人学习

XSL-FO 教程
XSL-FO 教程

共14课时 | 8.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号