1.概述
本文將著重于通過對系統中的所有實體使用單個通用的數據訪問對象來簡化DAO層 ,這將導致優雅的數據訪問 ,而不會造成不必要的混亂或冗長。
2. Hibernate和JPA DAO
大多數生產代碼庫都有某種DAO層。 通常,實現范圍從沒有抽象基類的多個類到某種通用類。 但是,一件事是一致的- 總是多于一件事-最有可能的是,DAO與系統中的實體之間存在一對一的關系。
同樣,根據所涉及泛型的級別,實際的實現方式可能從大量重復的代碼變為幾乎為空的代碼,大部分邏輯分組在基本抽象類中。
通常,可以通過使用Java Generics提供的類型安全性, 用單個參數化DAO替換這些多個實現,這樣就不會損失任何功能。
接下來介紹此概念的兩種實現 ,一種用于Hibernate中心持久性層,另一種針對JPA 。 這些實現絕不是完整的-僅包括某些數據訪問方法,但是可以很容易地使它們變得更徹底。
2.1。 抽象的休眠DAO
public abstract class AbstractHibernateDao< T extends Serializable > {private Class< T > clazz;@AutowiredSessionFactory 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。 通用休眠DAO
現在已經完成了抽象DAO,我們只需實現一次即可- 通用DAO實現將成為唯一需要的實現 :
@Repository
@Scope( BeanDefinition.SCOPE_PROTOTYPE )
public class GenericHibernateDao< T extends Serializable >extends AbstractHibernateDao< T > implements IGenericDao< T >{//
}
首先,請注意,通用實現本身是參數化的 -允許客戶根據具體情況選擇正確的參數。 這將意味著客戶端將獲得類型安全的所有好處,而無需為每個實體創建多個工件。
其次,注意這些通用DAO實現的原型范圍 。 使用此范圍意味著Spring容器將在每次請求DAO時(包括自動裝配時)創建一個新的DAO實例。 這將允許服務根據需要將具有不同參數的多個DAO用于不同的實體。
這個作用域如此重要的原因是由于Spring初始化容器中bean的方式。 將通用DAO保留為沒有作用域將意味著使用默認的singleton作用域 ,這將導致DAO的單個實例位于容器中。 對于任何一種更復雜的情況,這顯然都是主要的限制。
IGenericDao只是所有DAO方法的接口,因此我們可以將Spring的實現注入(或任何需要的)中:
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;@PersistenceContextEntityManager 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。 通用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;@Autowiredpublic void setDao( IGenericDao< Foo > daoToSet ){dao = daoToSet;dao.setClazz( Foo.class );}// ...}
Spring 使用setter注入自動裝配新的DAO實例,以便可以使用Class對象自定義實現。 在此之后,DAO已完全參數化,可供服務使用。
當然,還有其他方法可以為DAO指定類-通過反射,甚至以XML。 我偏向于這種簡單的解決方案,因為與使用反射相比,其可讀性和透明度更高。
4。結論
本文通過提供通用DAO的單個可重用實現,討論了數據訪問層的簡化 。 在基于Hibernate和JPA的環境中都介紹了此實現。 結果是簡化了的持久層,沒有不必要的混亂。
有關使用基于Java的配置和項目的基本Maven pom設置Spring上下文的分步介紹,請參閱本文 。
翻譯自: https://www.javacodegeeks.com/2011/12/simplifying-data-access-layer-with.html