Java 简单CRUD操作异常设计

Java 简单CRUD操作异常设计,java,exception,jdbc,crud,Java,Exception,Jdbc,Crud,我正在开发一个非常小的测试来模拟一个3层系统,这样我就可以理解异常是如何工作的。同时,我想提出一个合理的方法,以便我可以使用此设计作为其他应用中类似操作的进一步参考 我一直在阅读关于这个主题的不同文章,似乎对于使用检查或未检查的异常存在着巨大的争议,这让我对我的最终设计产生了怀疑 我将不讨论用于批评或支持检查/未检查异常的论点,因为它们可能都是众所周知的,但我将展示我的设计,寻找如何改进它并使其(尽可能长)类似于实际应用程序的一些建议 系统负责使用JDBC在关系数据库(比如MySQL)中执行基本

我正在开发一个非常小的测试来模拟一个3层系统,这样我就可以理解异常是如何工作的。同时,我想提出一个合理的方法,以便我可以使用此设计作为其他应用中类似操作的进一步参考

我一直在阅读关于这个主题的不同文章,似乎对于使用检查或未检查的异常存在着巨大的争议,这让我对我的最终设计产生了怀疑

我将不讨论用于批评或支持检查/未检查异常的论点,因为它们可能都是众所周知的,但我将展示我的设计,寻找如何改进它并使其(尽可能长)类似于实际应用程序的一些建议

系统负责使用JDBC在关系数据库(比如MySQL)中执行基本CRUD操作。我有以下几层:表示层、服务层和持久层

基于这个答案,我有理由不公开特定的层实现和解耦层。所以我决定创建我的自定义异常,并将它们包装到每个层的基本异常中,这样我就可以将特定层异常(即SQLException)转换为常规层异常(即PersistentException、BusinessException)。如果以后实现发生变化,我可以简单地将新的异常封装到更高层所期望的基本异常中。因此,我有以下例外:

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();
    }
现在,以下几点让我对这个设计感到不确定

  • 我喜欢让编译器验证 方法在使用checked时捕获/抛出声明的异常 例外情况。然而,同时我认为这种方法会导致 处理所有异常的混乱代码。不确定是否 这是因为我没有正确使用异常,或者 检查异常确实会导致代码混乱
  • 我还喜欢通过包装特定层来解耦层的想法 例外分为一般例外。但是,我可以看到很多新课程 为每个可能的异常创建,而不仅仅是抛出 现有的java异常
  • 我还可以看到,这个应用程序上的许多现有代码是 用于处理异常,其中一小部分用于 系统的实际目标
  • 这些实际上是我主要关心的问题,让我怀疑这是否真的是一个好的设计。我想听听您对此的看法(也许还有一些小的示例代码片段)。你们能给我一些提示,我该如何改进它吗?在某种程度上,我可以实现层之间的解耦,避免泄漏特定于层的问题

    我在这里的想法:
    关于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";
    }