Java 简单CRUD操作异常设计
我正在开发一个非常小的测试来模拟一个3层系统,这样我就可以理解异常是如何工作的。同时,我想提出一个合理的方法,以便我可以使用此设计作为其他应用中类似操作的进一步参考 我一直在阅读关于这个主题的不同文章,似乎对于使用检查或未检查的异常存在着巨大的争议,这让我对我的最终设计产生了怀疑 我将不讨论用于批评或支持检查/未检查异常的论点,因为它们可能都是众所周知的,但我将展示我的设计,寻找如何改进它并使其(尽可能长)类似于实际应用程序的一些建议 系统负责使用JDBC在关系数据库(比如MySQL)中执行基本CRUD操作。我有以下几层:表示层、服务层和持久层 基于这个答案,我有理由不公开特定的层实现和解耦层。所以我决定创建我的自定义异常,并将它们包装到每个层的基本异常中,这样我就可以将特定层异常(即SQLException)转换为常规层异常(即PersistentException、BusinessException)。如果以后实现发生变化,我可以简单地将新的异常封装到更高层所期望的基本异常中。因此,我有以下例外:Java 简单CRUD操作异常设计,java,exception,jdbc,crud,Java,Exception,Jdbc,Crud,我正在开发一个非常小的测试来模拟一个3层系统,这样我就可以理解异常是如何工作的。同时,我想提出一个合理的方法,以便我可以使用此设计作为其他应用中类似操作的进一步参考 我一直在阅读关于这个主题的不同文章,似乎对于使用检查或未检查的异常存在着巨大的争议,这让我对我的最终设计产生了怀疑 我将不讨论用于批评或支持检查/未检查异常的论点,因为它们可能都是众所周知的,但我将展示我的设计,寻找如何改进它并使其(尽可能长)类似于实际应用程序的一些建议 系统负责使用JDBC在关系数据库(比如MySQL)中执行基本
com.user.persistent.exceptions
|_ PersistentException
|_ ExistingUserException
|_ Some more..
com.user.business.exceptions
|_ BusinessException
|_ somemore....
摘自Josh Bloch的书《有效Java:“使用
已检查的异常情况,可合理预期调用者
恢复。“我也不太确定,但我相信用户可以从SQLExeption恢复(即用户错误地提供了现有ID,他可以重新键入正确的ID),因此我决定将前面的异常检查为异常。下面是这些类的概述:
持久层
public interface UserDAO
{
public void create(User team) throws PersistentException;
}
//Implementation in DefaultUserDAO.java
@Override
public void create(User team) throws PersistentException
{
try
{
System.out.println("Attempting to create an user - yikes the user already exists!");
throw new SQLIntegrityConstraintViolationException();
}
catch(SQLIntegrityConstraintViolationException e)
{
throw new ExistingUserException(e);
}
catch (SQLException e)
{
throw new PersistentException(e);
}
finally
{
//close connection
}
}
服务层:
public interface UserService
{
void create(User user) throws BusinessException;
}
//Implementation of UserService
@Override
public void create(User user) throws BusinessException
{
System.out.println("Doing some business logic before persisting the user..");
try
{
userDao.create(user);
}
catch (PersistentException e)
{
throw new BusinessException(e);
}
}
介绍
try
{
userService.create(user);
} catch (BusinessException e)
{
e.printStackTrace();
}
现在,以下几点让我对这个设计感到不确定
关于1-是的,选中的异常会添加“杂波代码”-这是一种折衷,
您需要考虑什么对您更重要
在许多设计中没有完美的解决方案,
你必须决定什么更适合你
关于业务例外-我个人不喜欢它
我想知道在客户端,当我添加用户时,它已经存在
我不想编写一个“Peals”BusinessException以获取根本原因的代码
还有一个一般性建议-请使用泛型作为crud异常
例如,不要使用
UserAlreadyExistsException
,而是使用EntityAlreadyExistsException
,我认为您的应用程序不应该在开发完成后处理SQL异常。因此,您不应该捕获像SQLException这样的异常。或者,如果您被迫捕获它们,那么只需重新调用RuntimeException。从我个人的经验来看,只有当你为别人开发某种类型的库时,创建自己的例外才有意义。即使在这种情况下,在许多情况下,您也可以使用现有的异常。尝试在不创建异常的情况下进行开发。只有当你意识到你离不开它们时才能创建它们。@Bartzilla:我也不太喜欢在每一层中包装和解包异常对象,它确实会让应用程序代码变得杂乱无章。
我认为错误代码和错误消息是一种更好的方法。
我认为有三种解决方案:
1) 在应用程序定义的RunTimeException类中包装DB层异常。此RuntimeException应该包含一个errorcode字段、一条错误消息和原始异常对象。因为所有DAO API都只会抛出运行时异常,所以这意味着业务层不必捕获它。它将被允许冒泡,直到处理它有意义为止。
例如
现在,按照这种方式,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) 让您的DAOAPI返回一个结果类型的对象,该对象包含与上面案例中DAOException中提供的信息相同的信息
所以你有一个结果类:-
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;
}
在上述情况下,约束是每个DAOAPI都需要返回相同的结果对象。当然,您的业务相关数据可以填充在ResultClass的不同子类中。
注意:在案例1和2中,都可以使用枚举来定义所有可能的错误代码。错误m
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;
}
@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";
}