JPA/Hibernate、Spring、Wicketに基づいて新しいアプリを設計しています。しかし、DAOとサービス層の違いは私にはそれほど明確ではありません。ウィキペディアによると、DAOは
ある種のデータベースまたは永続化メカニズムへの抽象インターフェースを提供し、データベースの詳細を公開せずに特定の操作を提供するオブジェクト。
DAOには、実際にはデータアクセスに多くの操作を行う必要はないが、クエリを使用して実行する方が簡単なメソッドを含めることができるかどうか疑問に思いました。たとえば、「特定の空港セットで運航しているすべての航空会社のリストを取得しますか?」サービス層メソッドのように思えますが、サービス層でJPA EntityManagerを使用することが良いプラクティスの例かどうかわかりませんか?
DAOは、単一のrelatedデータソースへのアクセスを提供する必要があり、ビジネスモデルの複雑さに応じて、本格的なビジネスオブジェクトまたは単純なデータオブジェクト。いずれにせよ、DAOメソッドはデータベースをある程度密接に反映する必要があります。
サービスは、ビジネスオブジェクトを処理するだけでなく、そもそもビジネスオブジェクトにアクセスするための高レベルのインターフェイスを提供できます。サービスからビジネスオブジェクトを取得する場合、そのオブジェクトは異なるデータベース(および異なるDAO)から作成される可能性があり、HTTPリクエストから作成された情報で装飾される可能性があります。いくつかのデータオブジェクトを単一の堅牢なビジネスオブジェクトに変換する特定のビジネスロジックを持つ場合があります。
私は一般に、そのデータベースまたはビジネス関連データのセットを使用するすべての人が使用するだろうと考えるDAOを作成します。これは、データベース内のトリガー、関数、ストアドプロシージャに加えて文字通り最低レベルのコードです。
特定の質問への回答:
DAOには、実際にはデータアクセスに多くの操作を行う必要はないが、クエリを使用して実行する方が簡単なメソッドを含めることができるかどうか疑問に思っていましたか?
ほとんどの場合、いいえ、サービスレイヤーのより複雑なビジネスロジック、個別のクエリからのデータのアセンブリが必要です。ただし、処理速度が心配な場合は、C++プログラマーが特定のアクションを高速化するためにアセンブラーコードを記述するのとほぼ同じ方法で、サービスレイヤーがアクションをDAOに委任する場合があります。
サービス層メソッドのように思えますが、サービス層でJPA EntityManagerを使用することが良いプラクティスの例かどうかわかりませんか?
サービスでエンティティマネージャを使用する場合は、エンティティマネージャをDAOと考えてください。それがまさにそれだからです。冗長なクエリビルディングを削除する必要がある場合は、サービスクラスで削除せずに、エンティティマネージャを利用するクラスに抽出し、DAOを作成します。ユースケースが非常に単純な場合、サービスレイヤーを完全にスキップしてエンティティマネージャーまたはコントローラーのDAOを使用できます。これは、サービスが行うことはgetAirplaneById()
への呼び出しをDAOのfindAirplaneById()
更新-以下の議論に関して明確にするために、サービスでエンティティマネージャを使用することは、コメントで強調されているさまざまな理由でDAO層もあるほとんどの状況で最良の決定ではない可能性があります。しかし、私の意見では、それは完全に合理的です:
例。
//some system that contains all our customers information
class PersonDao {
findPersonBySSN( long ssn )
}
//some other system where we store pets
class PetDao {
findPetsByAreaCode()
findCatByFullName()
}
//some web portal your building has this service
class OurPortalPetLostAndFoundService {
notifyOfLocalLostPets( Person p ) {
Location l = ourPortalEntityManager.findSingle( PortalUser.class, p.getSSN() )
.getOptions().getLocation();
... use other DAO's to get contact information and pets...
}
}
1つ確かなことがあります。サービスレイヤーでEntityManagerを使用する場合、daoレイヤーは必要ありません(実装の詳細を知る必要があるのは1つのレイヤーのみです)。それとは別に、さまざまな意見があります。
2番目のアプローチは、関心の分離に関してよりエレガントであり、1つの永続化テクノロジーから他の永続化テクノロジーへの切り替えを容易にします(新しいテクノロジーを使用してdaoインターフェースを再実装する必要があります)が、変更されます、最初の方が簡単です。
小さなプロジェクトがある場合は、サービス層でJPAを使用しますが、大規模なプロジェクトでは専用のDAO層を使用します。
これはAdam Bienによる 記事 が役に立つかもしれません。
従来は、サービスレイヤーとデータレイヤー間のコントラクトを定義するインターフェイスを記述していました。その後、実装を作成します。これらはDAOです。
例に戻ります。空港と航空会社の関係が多対多であり、airport_idとAirlines_idを含むテーブルがあると仮定すると、インターフェイスがあります。
public interface AirportDAO
{
public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports);
}
..そして、これのHibernate実装を提供するかもしれません。
public class HibernateAirportDAO implements AirportDAO
{
public List<Airline> getAirlinesOperatingFrom(Set<Airport> airports)
{
//implementation here using EntityManager.
}
}
AirlineエンティティにListを作成し、@ ManyToMany JPAアノテーションとの関係を定義することも検討できます。これにより、この特定のDAOメソッドを完全に持つ必要がなくなります。
また、DAOファクトリを記述するためのAbstract Factoryパターンを調べることもできます。例えば;
public abstract class DAOFactory
{
private static HibernateDAOFactory hdf = new HibernateDAOFactory();
public abstract AirportDAO getAirlineDAO();
public static DAOFactory getFactory()
{
//return a concrete implementation here, which implementation you
//return might depend on some application configuration settings.
}
}
public class HibernateDAOFactory extends DAOFactory
{
private static EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("myPersistenceUnit");
public static EntityManager getEM()
{
return emFactory.createEntityManager();
}
public AirportDAO getAirportDAO()
{
return new HibernateAirportDAO();
}
}
このパターンにより、HibernateDAOFactoryは単一のEMFを保持し、個々のDAOインスタンスにEMを提供することができます。依存性注入。
編集:いくつかの仮定を明確にしました。
Daoはデータアクセスオブジェクトです。データベース上のエンティティの保存/更新/選択を行います。エンティティマネージャオブジェクトがそのために使用されます(少なくともopen jpaでは)。このエンティティマネージャでクエリを実行することもできます。 sqlではなく、JPQL(Java Persistence Query Language)です。
簡単な例:
emf = Persistence.createEntityManagerFactory("localDB");
em = emf.createEntityManager();
Query q = em.createQuery("select u from Users as u where u.username = :username", Users.class);
q.setParameter("username", username);
List<Users> results = q.getResultList();
em.close();
emf.close();