SpringとJava GenericsでDAOを簡素化する
1. 概要
この記事では、システム内のすべてのエンティティに対して単一の生成されたデータアクセスオブジェクトを使用することにより、simplifying the DAO layerに焦点を当てます。これにより、不要な混乱や冗長性のない、洗練されたデータアクセスが実現します。
SpringとHibernateのour previous articleで見たAbstract DAOクラスに基づいて構築し、ジェネリックスのサポートを追加します。
参考文献:
Spring Data JPAの紹介
Spring 4を使用したSpring Data JPAの概要-Spring構成、DAO、手動および生成されたクエリとトランザクション管理。
JPA、Hibernate、およびSpring Data JPAを使用した監査
この記事では、アプリケーションに監査を導入するための3つのアプローチ、JPA、Hibernate Envers、およびSpring Data JPAについて説明します。
[more-722]#
2. HibernateおよびJPA DAO
ほとんどの製品コードベースには、ある種のDAO層があります。 通常、実装は、抽象基本クラスを持たない複数のクラスから、ある種の一般化されたクラスにまで及びます。 ただし、一貫していることが1つあります。それは、always more than oneが存在することです。 ほとんどの場合、DAOとシステム内のエンティティの間には1対1の関係があります。
また、関連するジェネリックのレベルに応じて、実際の実装は、重複したコードからほとんど空の状態まで変化し、大部分のロジックは基本抽象クラスにグループ化されます。
These multiple implementations can usually be replaced by a single parametrized DAO. Java Genericsが提供する型安全性を最大限に活用することで、機能が失われないようにこれを実装できます。
次に、この概念の2つの実装を示します。1つはHibernate中心の永続層用で、もう1つはJPAに焦点を当てています。 これらの実装は決して完全ではありませんが、含まれるデータアクセスメソッドを簡単に追加できます。
2.1. 抽象Hibernate DAO
AbstractHibernateDaoクラスを簡単に見てみましょう。
public abstract class AbstractHibernateDao {
private Class clazz;
@Autowired
SessionFactory sessionFactory;
public void setClazz(Class< T > clazzToSet){
this.clazz = clazzToSet;
}
public T findOne(long id){
return (T) getCurrentSession().get(clazz, id);
}
public List findAll() {
return getCurrentSession().createQuery("from " + clazz.getName()).list();
}
public T create(T entity) {
getCurrentSession().saveOrUpdate(entity);
return entity;
}
public T update(T entity) {
return (T) getCurrentSession().merge(entity);
}
public void delete(T entity) {
getCurrentSession().delete(entity);
}
public void deleteById(long entityId) {
T entity = findOne(entityId);
delete(entity);
}
protected Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
これは、エンティティを操作するためにSessionFactoryを使用する、いくつかのデータアクセスメソッドを持つ抽象クラスです。
2.2. ジェネリックHibernate DAO
抽象DAOクラスができたので、一度だけ拡張できます。 必要なThe generic DAO implementation will become the only implementation:
@Repository
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class GenericHibernateDao
extends AbstractHibernateDao implements IGenericDao{
//
}
まず、note that the generic implementation is itself parameterizedを使用して、クライアントがケースバイケースで正しいパラメーターを選択できるようにします。 つまり、クライアントは、エンティティごとに複数のアーティファクトを作成する必要なく、タイプセーフのすべての利点を享受できます。
次に、the prototype scope of this generic DAO implementationに注意してください。 このスコープを使用すると、Springコンテナは、要求されるたびに(自動配線を含む)、DAOの新しいインスタンスを作成します。 これにより、サービスは、必要に応じて、異なるエンティティに対して異なるパラメーターを持つ複数のDAOを使用できます。
このスコープが非常に重要な理由は、Springがコンテナ内のBeanを初期化する方法によるものです。 スコープなしで汎用DAOを残すことは、the default singleton scope, which would lead to a single instance of the DAO living in the containerを使用することを意味します。 それは明らかに、どんな種類のより複雑なシナリオでも主に制限されます。
IGenericDaoは、必要な実装を挿入できるように、すべてのDAOメソッドの単なるインターフェイスです。
public interface IGenericDao {
T findOne(final long id);
List 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
AbstractJpaDaoはAbstractHibernateDao:と非常によく似ています
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の実装と同様に、現在非推奨となっているSpringJpaTemplateに依存せずに、Java PersistenceAPIを直接使用しています。
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の注入
We now have a single DAO interface we can inject.Class:も指定する必要があります
@Service
class FooService implements IFooService{
IGenericDao dao;
@Autowired
public void setDao(IGenericDao daoToSet) {
dao = daoToSet;
dao.setClazz(Foo.class);
}
// ...
}
Classオブジェクトを使用して実装をカスタマイズできるように、autowires the new DAO instance using setter injectionを起動します。 この時点で、DAOは完全にパラメーター化され、サービスで使用できるようになります。
もちろん、クラスをDAOに指定できる他の方法があります-リフレクションを介して、またはXMLで。 私の好みは、反射を使用する場合と比べて読みやすさと透明度が向上しているため、この単純なソリューションを選択することです。
4. 結論
この記事では、汎用DAOの単一の再利用可能な実装を提供することにより、simplification of the Data Access Layerについて説明しました。 HibernateとJPAベースの両方の環境での実装を示しました。 その結果、不要な混乱のない、合理化された永続化レイヤーが得られます。
Javaベースの構成とプロジェクトの基本的なMavenpomを使用したSpringコンテキストのセットアップに関するステップバイステップの紹介については、this articleを参照してください。
最後に、この記事のコードはthe GitHub projectにあります。