6 に答える
開発が完了したら、アプリケーションで SQL 例外を処理するべきではないと思います。そのため、SQLException などの例外をキャッチしないでください。または、強制的にキャッチする必要がある場合は、RuntimeException を再スローします。私の名誉ある経験から、独自の例外を作成することは、他の誰かのためにある種のライブラリを開発する場合にのみ意味があります。この場合でも、多くの場合、既存の例外を使用できます。例外を作成せずに開発してみてください。それらなしではやっていけないことに気づいたときにのみ、それらを作成してください。
ここでの私の考え:
1 について - はい、チェックされた例外は「乱雑なコード」を追加します。これはトレードオフで
あり、自分にとって何がより重要かを考える必要があります。
多くのデザインでは完璧な解決策はなく、
自分に合ったものを決める必要があります。
BusinessExceptionについて - 私は個人的には好きではありません。
ユーザーを追加するときに、クライアント側で既に存在することを知りたいです。
根本原因を取得するために BusinessException を「処理」するようなコードは書きたくありません。
そして一般的な提案 - crud 例外には Generics を使用してください。
たとえば、 を使用せずUserAlreadyExistsException
にEntityAlreadyExistsException<User>
spring-jdbcがどのようにそれを行うかを見てください。とても良いデザインだと思います。Springは、ドライバーレイヤーからのチェックされた例外を処理し、チェックされていない例外をスローします。また、MySQL、Postgresなどのさまざまな例外を標準のSpring例外に変換します。
私は過去6年間、すべてのコードで未チェックの例外に切り替えましたが、振り返っていません。90%の確率で、その条件を処理できません。そして、あなたがそうする場合、あなたはそれについて、設計またはテストのいずれかによって知っており、関連するキャッチブロックを入れます。
構成上の慣例に基づいて、例外ハンドラーを設計できます。この目的のための最速で信頼できる方法は、AOP を使用することだと思います。この目的のために、例外の種類に基づいてアスペクトで例外を処理できます。回復の決定を下すことができます。例外からかどうか。たとえば、検証例外が発生した場合は、データを正しく入力するためにクライアントを既存のページに送信するための入力ページ パスを返すことができます。回復不能な例外が発生した場合は、例外ハンドラーでエラー ページを返すことができます。
たとえば、入力ページとエラー ページのグローバル名を指定して、入力ページとエラー ページの規則を作成できます。このようにして、例外の種類に基づいて適切なページにリクエストを送信することを決定できます。
アスペクト指向プログラミングについては、この投稿をフォローできます
以下にサンプルを追加
@Aspect
public class ExceptionHandlerAspect {
@Around("@annotation(ExceptionHandler)")
public Object isException(ProceedingJoinPoint proceedingJoinPoint) {
try {
return proceedingJoinPoint.proceed();
} catch (Exception exception) {
if (exception instanceof UnRecoverableException) {
addErrorMessage("global system error occurred");
return "errorPage";
} else if (exception instanceof ValidationException) {
addErrorMessage("validation exception occurred");
return "inputPage";
} else {
addErrorMessage("recoverable exception occurred");
return "inputPage";
}
}
}
}
@Target({METHOD})
@Retention(RUNTIME)
public @interface ExceptionHandler {
}
プレゼンテーション層
@ExceptionHandler
public String createUser() {
userService.create(user);
return "success";
}
@Bartzilla:私は、各レイヤーで例外オブジェクトをラップおよびアンラップすることもあまり好きではありません。アプリケーションコードが本当に乱雑になります。むしろ、エラー コードとエラー メッセージのアプローチの方がよいと考えています。この問題には 3 つの解決策があると思います。
1) アプリケーションで定義された RunTimeException クラスで DB 層の例外をラップします。この RuntimeException には、エラーコード フィールド、エラー メッセージ、および元の例外オブジェクトが含まれている必要があります。すべての DAO API はランタイム例外のみをスローするため、ビジネス レイヤーは必ずしもそれをキャッチする必要はありません。それを処理するのが理にかなっているポイントまで泡立つことができます。例えば
class DAOException extends RuntimeException{
private int errorCode;
private String errorMessage;
private Exception originalException;
DAOException(int errorCode, String errorMessage, Exception originalException){
this.errorCode=errorCode;
this.errorMessage=errorMessage;
this.originalException=originalException;
}
}
このようにして、DAO メソッドは例外に基づいてエラー コードを決定します。
int Integrity_Voildation_ERROR=3;
public void create(User team) throws DAOException
{
try
{
System.out.println("Attempting to create an user - yikes the user already exists!");
throw new SQLIntegrityConstraintViolationException();
}
catch(SQLIntegrityConstraintViolationException e)
{ int errorCode=Integrity_Voildation_ERROR;
throw new DAOException(Integrity_Voildation_ERROR,"user is already found",e);
}
}
この例外は、本来あるべきレイヤーでキャッチできます。このシナリオでは、各エラー コードは回復可能な (アクション可能な) 例外を意味します。もちろん、アプリケーション (サーブレットやフィルターなど) へのエントリ ポイントは、一般的な例外をキャッチして回復不能な例外をキャッチし、意味のあるエラーをユーザーに表示する必要があります。
2) DAO API が、上記の場合に DAOException で提供されたものと同じ情報を含む Result 種類のオブジェクトを返すようにします。
だからあなたは Result クラスを持っています:-
Class Result implements IResult{
private boolean isSuccess;
private int errorCode;
private String errorMessage;
private Exception originalException;//this might be optional to put here.
}
So a DAO API:
public IResult create(User team) throws DAOException
{
IResult result=new Result();
try
{
System.out.println("Attempting to create an user - yikes the user already exists!");
throw new SQLIntegrityConstraintViolationException();
}
catch(SQLIntegrityConstraintViolationException e)
{ int errorCode=Integrity_Voildation_ERROR;
result.setSuccess(false);
result.setErrorCode(errorCode);
result.setErrorMessage("user is already found");
}
return result;
}
上記の場合、制約は、各 DAO API が同じ結果オブジェクトを返す必要があることです。もちろん、ビジネス関連のデータは、ResultClass のさまざまなサブクラスに入力できます。ケース 1 と 2 の両方で、考えられるすべてのエラー コードを定義するために列挙型を使用できることに注意してください。エラー メッセージは、データベース テーブルなどから取得できます。
3) errorCode の使用を避けたい場合は、以下を実行できます。 DAOException (上記のケース 1) のような単一の RunTimeException クラスを定義する代わりに、回復可能な SQL 例外の可能なタイプごとに例外階層を定義できます。それぞれが親クラス DAOException のサブクラスです。
同じことの優れた例は、Java ベースの Spring フレームワーク DAO 例外階層によって行われます。これを通過してください。
あなたは 3 層システムと言いますが、これは、これらの層が異なるコンピューターまたは異なるプロセスで実行される可能性があることを意味しますか? それとも、特定の機能によって定義された一連のレイヤーにコードを整理し、すべてを単一のノードで実行しているだけですか?
これを尋ねる理由 - レイヤーが異なるプロセスで実行されていない限り - CheckedExceptions を持っていても何の価値もありません。多くの CheckedExceptions があると、コードが乱雑になり、不要な複雑さが増します。
例外の設計は重要であり、ag112 の使用が上記で提案した方法のようなことを行うこと、つまり RunTimeException の使用はクリーンで、コードが少なく、管理しやすいです。
..ただの私の考え