1.概述
本文将重点介绍如何通过为系统中的所有实体使用单个,通用的数据访问对象来简化DAO层,这将导致优雅的数据访问,没有不必要的混乱或冗长。
2. Hibernate和JPA DAO
大多数生产代码库都有某种DAO层。通常,实现的范围从没有抽象基类的多个类到某种类的泛化类。但是,有一点是一致的 - 总有不止一个 - 最有可能的是,DAO与系统中的实体之间存在一对一的关系。
此外,根据所涉及的泛型的级别,实际的实现可以从高度重复的代码变为几乎为空,其中大部分逻辑分组在基本抽象类中。
这些多个实现通常可以由单个参数化DAO替代,因为通过充分利用Java Generics提供的类型安全性,不会丢失任何功能。
接下来介绍这个概念的两个实现,一个用于Hibernate中心持久层,另一个用于JPA。这些实现绝不是完整的 - 只包括一些数据访问方法,但可以更容易地使它们更加彻底。
2.1。抽象Hibernate DAO
public abstract class AbstractHibernateDao< T extends Serializable > {
private Class< T > clazz;
@Autowired
SessionFactory sessionFactory;
public final void setClazz( Class< T > clazzToSet ){
this.clazz = clazzToSet;
}
public T findOne( long id ){
return (T) getCurrentSession().get( clazz, id );
}
public List< T > findAll(){
return getCurrentSession().createQuery( "from " + clazz.getName() ).list();
}
public void create( T entity ){
getCurrentSession().persist( entity );
}
public void update( T entity ){
getCurrentSession().merge( entity );
}
public void delete( T entity ){
getCurrentSession().delete( entity );
}
public void deleteById( long entityId ) {
T entity = findOne( entityId );
delete( entity );
}
protected final Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
DAO直接使用Hibernate API,而不依赖于任何Spring模板(例如HibernateTemplate)。在Hibernate DAO教程中介绍了模板的使用以及在DAO中自动装配的SessionFactory的管理。
2.2。通用Hibernate DAO
现在抽象DAO已经完成,我们只需实现一次 - 通用DAO实现将成为唯一需要的实现:
@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericHibernateDao< T extends Serializable >
extends AbstractHibernateDao< T > implements IGenericDao< T >{
//
}
首先,请注意,泛型实现本身已参数化 - 允许客户端根据具体情况选择正确的参数。这意味着客户端可以获得类型安全的所有好处,而无需为每个实体创建多个工件。
其次,请注意此通用DAO实现的原型范围。使用此作用域意味着Spring容器将在每次请求时创建DAO的新实例(包括自动装配)。这将允许服务根据需要为不同的实体使用具有不同参数的多个DAO。
这个范围如此重要的原因是Spring在容器中初始化bean的方式。保留没有范围的通用DAO意味着使用默认的单例范围,这将导致生活在容器中的DAO的单个实例。对于任何更复杂的情况,这显然是主要限制因素。
该IGenericDao简直就是所有的DAO方法的接口,使我们可以在(或任何需要)注入我们与春天的实现:
public interface IGenericDao<T extends Serializable> {
T findOne(final long id);
List<T> findAll();
void create(final T entity);
T update(final T entity);
void delete(final T entity);
void deleteById(final long entityId);
}
2.3。本摘要 JPA DAO
public abstract class AbstractJpaDao< T extends Serializable > {
private Class< T > clazz;
@PersistenceContext
EntityManager entityManager;
public void setClazz( Class< T > clazzToSet ) {
this.clazz = clazzToSet;
}
public T findOne( Long id ){
return entityManager.find( clazz, id );
}
public List< T > findAll(){
return entityManager.createQuery( "from " + clazz.getName() )
.getResultList();
}
public void save( T entity ){
entityManager.persist( entity );
}
public void update( T entity ){
entityManager.merge( entity );
}
public void delete( T entity ){
entityManager.remove( entity );
}
public void deleteById( Long entityId ){
T entity = getById( entityId );
delete( entity );
}
}
与Hibernate DAO实现类似,此处直接使用Java Persistence API,同样不依赖于现已弃用的 Spring JpaTemplate。
2.4。Generic JPA DAO
与Hibernate实现类似,JPA数据访问对象也很简单:
@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericJpaDao< T extends Serializable >
extends AbstractJpaDao< T > implements IGenericDao< T >{
//
}
3.注入这个DAO
现在有一个由Spring注入的DAO ; 此外,类需要指定:
@Service
class FooService implements IFooService{
IGenericDao< Foo > dao;
@Autowired
public void setDao( IGenericDao< Foo > daoToSet ){
dao = daoToSet;
dao.setClazz( Foo.class );
}
// ...
27*6
}
Spring 使用setter注入自动装配新的DAO实例,以便可以使用Class对象自定义实现。在此之后,DAO完全参数化并准备好供服务使用。
当然还有其他方法可以为DAO指定类 - 通过反射,甚至是XML。我倾向于采用这种更简单的解决方案,因为与使用反射相比,可读性和透明度得到了提高。