純粋なJDBCで動作するアプリケーションがあります。私はジレンマがあります トランザクション処理 行くべきServiceまたは[〜# 〜] dao [〜#〜]レイヤー。 DAOはできるだけシンプルである必要があり、データベースへの接続を提供するためだけに存在するため、ほとんどの場合、それは サービスレイヤーで実装する必要があります であることがわかりました。私はこのアプローチが好きですが、私が見つけたすべてのソリューションと例はSpringまたは他のフレームワークで機能し、注釈を使用して@Transactional
としてマークします。
私のDAOレイヤーの純粋なJDBCにはDaoFactory
があり、各DAOクラス(UserDao
、CarDao
)。接続プーリングを実装し、このオブジェクトを使用してデータベースに接続し、CRUD操作を実行します。 Serviceレイヤーで、必要な特定のDAOのインスタンスを作成し、その上でアクション/計算を実行します。
ここでトランザクション処理をどこに実装しますか?
かなり長い回答で申し訳ありません。
現代のアプリケーションアプローチでは、接続を工場から取得するのではなく、DAOに注入することを検討する必要があると思います。
あなたの問題は、私が理解しているように、サービスレイヤーの観点から、サービスレイヤーでのより複雑なデータベース操作に組み合わせる必要がある小さな粒度のDAO機能(CRUD)が必要であることです。 dbトランザクションを使用して複雑な操作の整合性を確保したい。発生する問題は、DAOクラスの外部でのトランザクション処理とデータベースアクセスの可視性です。ビジネスコードがトランザクションコードと接続処理コードで汚れてしまい、BLの邪魔になってしまいます。
これはORMベースのアプローチでも共通の問題です。たとえば、単純なHibernateアプリケーションでは、すでに実行中のセッションにいるか、新しいセッションを開始する必要があるかを決定する必要があります。休止状態のコードを別のクラスにカプセル化する場合、コードの重複を避けるために、セッションの開始と終了をスマートな方法で処理する必要があります。
もう1つの問題は、メソッドのスタッキングです。各メソッドが接続/トランザクションを処理する場合、あるメソッドから別のメソッドを簡単に呼び出すことはできません。
私はあなたのために考えたいかもしれないいくつかのアイデアを持っています:
スタッキングの問題の最初:
これにより、コードの任意のレベルでトランザクションと接続を開始できます。データベースに関してbeginTransactionを呼び出すことはべき等ですが、カウンターが発生します。コードのコミットをスキップしないように、十分に注意する必要があります。そうしないと、トランザクション境界が実行されません。
このメソッドを使用すると、obtainConnection/startTransaction/commit/rollback/returnConnection呼び出しにより、すべてのレベルでコードが乱雑になります。ここには定型文がたくさんあります。
しかし、この方法を念頭に置いて、私はこれで私のフレームワークのスタッキング問題を解決しました。ここで、この接続の混乱をコードから削除する方法を見つける必要があります。 3番目のアプローチに重点を置いて、以下のアプローチが有用であることを確認してください。
接続の依存性注入: DAOはシングルトンではなく、使い捨てオブジェクトであり、作成時に接続を受け取ります。呼び出しコードが接続の作成を制御します。
接続/トランザクションを実行中のスレッドにバインドします: DAOはシングルトンにすることができます。ランタイム情報は、スレッドローカルコンテキストオブジェクトにバインドします。ビジネス層は、DAOのコンテキストを初期化します。 Webアプリケーションの場合、これはリクエストの開始時に発生する可能性があります。
プロキシクラスを使用して、サービスメソッドの呼び出し中にスレッド内のコンテキストオブジェクトに透過的に接続処理を実装します。
私は3つすべてのアプローチを実装しました(Spring etalなし)。最後の1つは特に非常に強力であり、サービスインフラストラクチャを大幅にクリーンアップします。あなたはフレームワークを分かりやすく説明します-するのは楽しいです:-)
しかし、必要に応じて、いくつかのプロジェクトによって提供される軽量フレームワークを使用したほうがよい場合があります。
トランザクションのないサービス関数内にトランザクション境界を実装する必要がある場合は、次のことができます。
通常、サービス境界にトランザクション境界を設定することをお勧めします。ビジネスルールを実装するために、複数のDAO(または他の中間層)を必要とするサービスを簡単に作成できます。
注意として、これをプログラムで自分で行うべきではありません。 SpringとJEEは、これをそのまま使用できます。
私はプロジェクトに参加しており、人々は春のクールエイドを飲みません...私は以下のパターンが私にうまくいくことを発見しました...
Service(A singleton)-->*Manager( singletons )-->DAOs
単一のトランザクション内で更新する必要がある3つの異なるテーブルがあるとします(すべてまたは何もない)。テーブルの1つは「親」テーブルで、親の保存中に更新する必要がある子テーブルがあります。
public class ParentServive
private ParentManager parentManager;
public void save(Parent parent){
parentManager.save(parent)
}
}
すべての* Managerクラスが拡張するDAOManagerを作成する
public class DAOManager{
...
..
public Connection getConnection()throws DAOException{
try{
dataSource.getConnection();
}catch(SQLException e){
throw new DAOExceptoin(e);
}
}
public Connection getTXConnection()throws DAOException{
Connection connection = dataSource.connection;
connection.setAutoCommit(false);
return connection;
}
public void close(Connection connectoin) throws DAOException{
try{
if ( !connection.getAutoCommit() ){
connection.setAutoCommit(true);
}
connection.close();
}catch(SQLException e){
throw new DAOExceptoin(e);
}
}
}
これで、保存を有効にするために親テーブルを更新する必要がある2つのDAOが最初に作成され、抽象DAOManagerが作成されます。1つの接続のみを開き、すべてのテーブルでロールバックまたはコミットできます...
public class ParentManager extends DAOManager{
public static getInstance(){
..
...
}
public save(Parent parent) throw DAOException{
Connection connection = null;
try{
connection = getTXConnection();
ParentDAO parentDAO = new ParentDAO(connection);
ChildOneDAO childOneDAO = new ChildOneDAO(connection);
ChildTwoDAO childTwoDAO = new ChildTwoDAO(connection);
parentDAO.save(...)
childOneDAO.save(...)
childTwoDAO.save(..)
connection.commit();
}catch(Exception e){
connection.rollback();
}finally{
close(connection);
}
}
}
次に、サービスクラスはManagerクラスのみを使用する必要があります。接続管理について心配する必要はありません... Managerは接続を管理し、使用するDAOをその場で作成します。