6 回答
我相信您的应用程序在开发完成后不应该处理 SQL 异常。所以,你不应该捕获像 SQLException 这样的异常。或者,如果您被迫捕获它们,则只需重新抛出 RuntimeException。根据我的荣誉经验,创建自己的异常只有在为其他人开发某种库时才有意义。即使在这种情况下,在许多情况下您也可以使用现有的异常。尝试在不创建异常的情况下进行开发。只有当你意识到你不能没有它们时才创建它们。
我的想法在这里:
关于 1 - 是的,检查的异常会添加“混乱代码” - 这是一个权衡,
你需要考虑什么对你来说更重要。
在许多设计中,没有完美的解决方案,
您必须决定什么更适合您。
关于 BusinessException - 我个人不喜欢它。
我想在客户端知道,当我添加用户时,它已经存在。
我不想编写“Peals” BusinessException 的代码来获取根本原因。
和一个一般性的建议 - 请使用泛型作为 crud 异常。
例如,不要使用UserAlreadyExistsException
,而是使用EntityAlreadyExistsException<User>
@Bartzilla:我也不喜欢在每一层中包装和展开异常对象,它确实使应用程序代码混乱。我宁愿认为错误代码和错误消息方法是更好的方法。对于这个问题,我认为有三种解决方案:
1) 在应用程序定义的 RunTimeException 类中包装 DB 层异常。这个 RuntimeException 应该包含一个 errorcode 字段、一个错误消息和原始异常对象。由于你所有的 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);
}
}
这个异常可以在它应该在的层中被捕获。在这种情况下,每个错误代码都意味着一个可恢复(可操作)的异常。当然,应用程序的入口点(servlet 或过滤器或任何东西)必须捕获一般异常以捕获不可恢复的异常并向用户显示有意义的错误。
2)让您的 DAO API 返回一个 Result 类型的对象,该对象包含与上述情况下 DAOException 中提供的相同信息。
所以你有 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(上面的case1)那样定义单个RunTimeException类,你可以为每种可能的可恢复SQL异常类型定义一个异常层次结构,每个都是父类DAOException的子类。
基于 java 的 Spring 框架 DAO 异常层次结构就是一个很好的例子。请通过这个。
基于约定优于配置概念,您可以设计您的异常处理程序,我认为为此目的最快和可靠的方法是使用 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";
}
看看 spring-jdbc 是如何做到的。我认为这是一个非常好的设计。Spring 处理来自驱动层的已检查异常并抛出未检查异常。它还将来自 MySQL、Postgres 等的不同异常转换为标准 Spring 异常。
在过去的 6 年里,我在所有代码中都切换到了未经检查的异常,并且没有回头。90%的时间你无法处理代码。并且您通过设计或测试了解它的情况,并将相关的 catch 块放入。
您说 3 层系统 - 这是否意味着这些层可能在不同的计算机或不同的进程中运行?还是只是将代码组织成一组由特定功能定义的层,并且所有东西都在单个节点上运行?
问这个的原因 - 除非你的层在不同的进程中运行 - 它没有增加 CheckedExceptions 的价值。拥有大量 CheckedExceptions 只会使代码混乱并增加不必要的复杂性。
异常设计很重要,并且按照上面建议的使用 ag112 的方式进行操作,即使用 RunTimeException 是干净的、更少的代码和可管理的。
..只是我的想法