【问题标题】:Eclipse - GWT - Java - MySQL - how to catch an exception correctlyEclipse - GWT - Java - MySQL - 如何正确捕获异常
【发布时间】:2014-05-11 01:02:33
【问题描述】:

我终于完成了我的应用程序(Eclipse、GWT、Java、MySQL、Tomcat),它已上传到服务器上(我让其他人将应用程序上传到服务器上)。但是,服务器安装似乎存在问题,我的代码没有发回任何错误。

例如:创建新帐户时,会显示以下消息“您的帐户已创建。请联系领导将青年成员关联到该帐户。”但是数据库没有更新。看来我没有正确捕获异常。

我的代码是:

客户端调用:

AsyncCallback<User> callback = new CreationHandler<User>();
rpc.createUser(textBoxAccount.getText(), textBoxPassword.getText(), null, null, null, callback);

服务器端:

public User createUser(String userName, String pass, String level, String pack, java.sql.Date archived) {
    User user = null; // necessary unless you do something in the exception handler
    ResultSet result = null;
    PreparedStatement ps = null;
    String pw_hash = BCrypt.hashpw(pass, BCrypt.gensalt());
    try {
      ps = conn.prepareStatement(
          "INSERT INTO at_accounts (acc_email_address, acc_password, acc_enabled) " +
                  "VALUES (?, ?, ?)");
      ps.setString(1, userName);
      ps.setString(2, pw_hash);
      ps.setString(3, "1");
      ps.executeUpdate();
    }
    catch (SQLException e) {
      //do stuff on fail
        System.out.println("SQLException createUser 1.");
        e.printStackTrace();
        user = null;
    }
    finally {
        if (result != null) {
            try {
                result.close();
            }
            catch (SQLException e) {
                System.out.println("SQLException createUser 2.");
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            }   
            catch (SQLException e) {
                System.out.println("SQLException createUser 3.");
                e.printStackTrace();
            }
        }
    }
    return user;
}

客户端:

class CreationHandler<T> implements AsyncCallback<User> {
    //Create the account.
    public void onFailure(Throwable ex) {
        Window.alert("RPC call failed - CreationHandler - Notify Administrator.");  
    }
    public void onSuccess(User result) {
        Window.alert("Your account has been created. Please contact a leader to associate youth members to it.");

    }
}

任何帮助将不胜感激。

问候,

格林

嗨,乔恩克,

请问你是这个意思吗?

public User createUser(String userName, String pass, String level, String pack, java.sql.Date archived) {
    User user = null; // necessary unless you do something in the exception handler
    ResultSet result = null;
    PreparedStatement ps = null;
    String pw_hash = BCrypt.hashpw(pass, BCrypt.gensalt());
    try {
      ps = conn.prepareStatement(
          "INSERT INTO at_accounts (acc_email_address, acc_password, acc_enabled) " +
                  "VALUES (?, ?, ?)");
      ps.setString(1, userName);
      ps.setString(2, pw_hash);
      ps.setString(3, "1");
      ps.executeUpdate();
    }
    catch (SQLException e) {
      //do stuff on fail
        try {
            conn.rollback();
        } catch (SQLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.out.println("SQLException createUser 1.");
        e.printStackTrace();
        user = null;
    }
    finally {
        if (result != null) {
            try {
                result.close();
            }
            catch (SQLException e) {
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.out.println("SQLException createUser 2.");
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            }   
            catch (SQLException e) {
                try {
                    conn.rollback();
                } catch (SQLException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                System.out.println("SQLException createUser 3.");
                e.printStackTrace();
            }
        }
    }

    try {
        conn.commit();
    } catch (SQLException e) {
        try {
            conn.rollback();
        } catch (SQLException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        System.out.println("SQLException createUser 4 - commit error.");
        e.printStackTrace();
    }
    return user;
}

这是带有建议的错误处理的更新代码:

package org.AwardTracker.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

import org.AwardTracker.client.BCrypt;

import org.AwardTracker.client.Account;
import org.AwardTracker.client.AccountAndCubs;
import org.AwardTracker.client.AccountCubAssociation;
import org.AwardTracker.client.AwardAward;
import org.AwardTracker.client.AwardDescription;
import org.AwardTracker.client.AwardStockDtls;
import org.AwardTracker.client.DBConnection;
import org.AwardTracker.client.SectionDetails;
import org.AwardTracker.client.Stock;
import org.AwardTracker.client.User;
import org.AwardTracker.client.ViewData;
import org.AwardTracker.client.YMATask;
import org.AwardTracker.client.YMAwards;
import org.AwardTracker.client.YMandAward;
import org.AwardTracker.client.YMAwardDetails;
import org.AwardTracker.client.YouthMember;
import org.AwardTracker.client.YouthMemberAwards;
import org.AwardTracker.client.YthMmbrSectDtls;

import org.AwardTracker.server.Base64Encode2;

public class MySQLConnection extends RemoteServiceServlet implements DBConnection {
//TODO
//  •Use JNDI to bind the data source.
//  •Close the connection as soon as its done in finally block.
//  •Manage the connection in single class for whole application.
//  •Initialise the data source at application start up single time.
//  •Store the database configuration outside the JAVA code somewhere in properties file or web.xml.
//  •Create an abstract class for AsyncCallback that will handle all the failures happened while performing any RPC calls.
//  •Extend this abstract class for all RPC AsyncCallback but now you have to just provide implementation of onSuccess() only.
//  •Don't handle any exception in service implementation just throw it to client or if handled then re-throw some meaning full exception back to client.
//  •Add throws in all the methods for all the RemoteService interfaces whenever needed.

private static final long serialVersionUID = 1L;
private Connection conn = null;
private String url = "jdbc:mysql://localhost/awardtracker";
private String user = "awtrack";
private String pass = "************";

public MySQLConnection() {
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection(url, user, pass);
    } catch (Exception e) {
        //NEVER catch exceptions like this

        System.out.println("Error connecting to database - not good eh");
        e.printStackTrace();
    }
}

//Store and retrieve data used by Views within the application
//This allows us to securely pass parameters between Views.
private ViewData viewData = null;

public ViewData setViewData(String accountId, String accountLevel,
        String ymId, String awId, String adGroup) {

    viewData = new ViewData();

    viewData.setaccountId(accountId);
    viewData.setaccountLevel(accountLevel);
    viewData.setymId(ymId);
    viewData.setawId(awId);
    viewData.setadGroup(adGroup);

    return viewData;
}

public ViewData getViewData() {
    return viewData;
}


public User authenticateUser(String accID, String userName, String pass, String level, String pack, Integer enabled, java.sql.Date archived) {

    User user = null; // necessary unless you do something in the exception handler
    ResultSet result = null;
    PreparedStatement ps = null;
    String stored_hash = null;

    try {
        ps = conn.prepareStatement(
          "SELECT * " +
          "FROM at_accounts " +
          "WHERE acc_email_address = ?");

        ps.setString(1, userName);

        result = ps.executeQuery();

        while (result.next()) {
            user = new User(result.getString(1), result.getString(2), result.getString(3), result.getString(4), result.getString(5), result.getInt(6), result.getDate(7));
            stored_hash = result.getString(3);
        }
    }
    catch (SQLException e) {
        try {
            conn.rollback();
        } 
        catch (SQLException e2) {
            System.out.println("Error rolling back transaction for authenticateUser.");
            e2.printStackTrace();
        }
        System.out.println("SQLException in authenticateUser.");
        e.printStackTrace();
    }

    if (stored_hash != null) {
        if (BCrypt.checkpw(pass, stored_hash))  {
        } else {
            user = null;
        }
    }else{
        user = null;
    }

    return user;
}

//Disable or enable Account
public User disableUser(String user, Integer enabled) {
    PreparedStatement ps = null;

    try {
      ps = conn.prepareStatement(
              "UPDATE at_accounts " +
              "SET acc_enabled=? " +
              "WHERE acc_email_address=?");

      ps.setInt(1, enabled);
      ps.setString(2, user);
      ps.executeUpdate();
      conn.commit();
    }
    catch (SQLException e) {
        try {
            conn.rollback();
        } 
        catch (SQLException e2) {
            System.out.println("Error rolling back transaction for createUser.");
            e2.printStackTrace();
        }
        System.out.println("SQLException in createUser.");
        e.printStackTrace();
    }

    return null;
}

public User duplicateUser(String userName, String pass, String level, String pack, java.sql.Date archived) {
    User user = null; // necessary unless you do something in the exception handler
    ResultSet result = null;
    PreparedStatement ps = null;
    try {
      ps = conn.prepareStatement(
          "SELECT * " +
          "FROM at_accounts " +
          "WHERE acc_email_address = ?");

      ps.setString(1, userName);

      result = ps.executeQuery();
      while (result.next()) {
         user = new User(null, result.getString(2), null, null, null, null, null);
      }
    }
    catch (SQLException e) {
        try {
            conn.rollback();
        } 
        catch (SQLException e2) {
            System.out.println("Error rolling back transaction for duplicateUser.");
            e2.printStackTrace();
        }
        System.out.println("SQLException in duplicateUser.");
        e.printStackTrace();
    }
    return user;
}

public User createUser(String userName, String pass, String level, String pack, java.sql.Date archived) {
    PreparedStatement ps = null;
    String pw_hash = BCrypt.hashpw(pass, BCrypt.gensalt());
    try {
      ps = conn.prepareStatement(
          "INSERT INTO at_accounts (acc_email_address, acc_password, acc_enabled) " +
                  "VALUES (?, ?, ?)");
      ps.setString(1, userName);
      ps.setString(2, pw_hash);
      ps.setString(3, "1");
      ps.executeUpdate();
      conn.commit();
    }
    catch (SQLException e) {
        try {
            conn.rollback();
        } 
        catch (SQLException e2) {
            System.out.println("Error rolling back transaction for createUser.");
            e2.printStackTrace();
        }
        System.out.println("SQLException in createUser.");
        e.printStackTrace();
    }

    return null;
}

【问题讨论】:

  • 是否记录了三个异常?
  • 嗨,Braj,我已经向设置服务器的人询问了这个问题,但尚未收到回复。我必须要有耐心,因为他在自己的时间里进行设置,以帮助我们集团。问候,格林。
  • 我会一步一步来。我现在已经发布了带有新错误处理的更改。请检查以确保这是您的意思。接下来我会将错误处理移至抽象类(需要将参数传递给它,以便我知道哪个类返回错误),查看 log4j 并将 DB 登录移动到 XML。问候,格林。

标签: java mysql eclipse tomcat gwt


【解决方案1】:

要记住的要点:

  • 使用 JNDI 绑定数据源。
  • finally 块中完成后立即关闭连接。
  • 在单个类中管理整个应用程序的连接。
  • 在应用程序启动时初始化数据源。
  • 将数据库配置存储在 JAVA 代码之外的某个位置的属性文件或 web.xml 中。

我已经分享了 ConnectionUtil 类的示例代码,其唯一目的是使用 JNDI 查找 管理单个类中的连接,它可以记录有多少连接在应用程序中打开的时间是什么时候?

请看下面的帖子:


GWT - 如何正确捕获异常?

  • AsyncCallback 创建一个抽象类,用于处理在执行任何 RPC 调用时发生的所有故障。
  • 为所有 RPC AsyncCallback 扩展这个抽象类,但现在您只需提供 onSuccess() 的实现即可。
  • 不要在服务植入中处理任何异常,只需将其抛出给客户端,或者如果已处理则重新将一些有意义的完整异常返回给客户端。
  • 在需要时在所有RemoteService 接口的所有方法中添加throws

示例代码:

// single class to handle all the AsyncCallback failure
public abstract class MyAsyncCallback<T> implements AsyncCallback<T> {

    @Override
    public void onFailure(Throwable caught) {
        // all the failure are catched here
        // prompt user if needed
        // on failure message goes to here
        // send the failure message back to server for logging
    }

}

// do it for all the RPC AsyncCallback
public class CreationHandler<T> extends MyAsyncCallback<T> {
    //Create the account.
    public void onSuccess(T result) {
        // on success message goes to here
    }
}

// use in this way
AsyncCallback<User> callback = new CreationHandler<User>();

【讨论】:

  • 嗨,Braj,谢谢。我需要一些时间来消化并解决它。问候,格林。
  • @Glyn 我已经更新了我的帖子以在单个类中处理 GWT 中的所有 RPC 异常。
  • 嗨,Braj,谢谢。我已经更新了我的代码以包含来自 JonK 的正确错误处理。接下来,我会将错误处理移至您的单个类。问候,格林。
  • 嗨,Braj,我将上面的代码包含在服务器端,然后让我感到震惊的是,这与我在客户端使用的代码相同。我想我完全想念你在说什么。所以我认为我在客户端做正确的事情是捕获从服务器端发回的消息并在适当的时候显示错误消息。我需要做的就是让我的服务器端代码正确。那正确吗?问候,格林。
  • 假设您使用不同类型的 AsyncCallbacks 进行 100 次 RPC 调用,那么您必须为所有调用覆盖 onFailure() 方法,但通过扩展我在帖子中提供的单个类 MyAsyncCallback 将负责onFailure() 方法,你不需要为所有 100 个不同类型的 AsyncCallbacks 的 RPC 调用实现 onFailure()。我希望你现在明白了。我再次总结一下针对整个应用程序的所有 RPC 调用的单个 onFailure() 方法。
【解决方案2】:

您没有将事务提交到数据库。为了使ps.executeUpdate(); 所做的更改永久生效,您需要在更新后致电conn.commit();

同样,在您的catch 块中,您应该调用conn.rollback(); 以避免将无效数据插入数据库的可能性。

我看不到conn 的声明,所以我假设它是一个成员变量,无论createUser 属于哪个类。您可能需要考虑将 Connection 更改为方法中的本地,这样您就不会忘记在不再需要它时关闭它(应该在您提交后) .

最后,如果您使用的是 Java 7+,您可以利用 try-with-resources 为您处理您的 PreparedStatementResultSetConnection 的关闭(尽管您似乎没有将ResultSet 用于任何事情,因此请考虑将其从方法中删除)。


这里有两个例子来说明我的意思(一个用于 Java 6 及更低版本,一个用于 Java 7 及更高版本,使用try-with-resources

Java 6-

public void createUser(String userName, String pass) {
    PreparedStatement ps = null;
    Connection conn = null;
    String pw_hash = BCrypt.hashpw(pass, BCrypt.gensalt());
    try {
        // Acquire a Connection here rather than using a member variable
        // NOTE: See Braj's answer for a better way of doing this
        // using his ConnectionUtil class.
        conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost/awardtracker", "awtrack", 
                    "**************");

        ps = conn.prepareStatement(
                "INSERT INTO at_accounts (acc_email_address, acc_password,"
                  + " acc_enabled) "
              + "VALUES (?, ?, ?)");
        ps.setString(1, userName);
        ps.setString(2, pw_hash);
        ps.setString(3, "1");
        ps.executeUpdate();
        conn.commit();
    } catch (SQLException e) {
        try {
            conn.rollback();
        } catch (SQLException e2) {
            System.out.println("Error rolling back transaction.");
            e2.printStackTrace();
        }
        System.out.println("SQLException createUser 1.");
        e.printStackTrace();
    } finally {
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                System.out.println("SQLException createUser 3.");
                e.printStackTrace();
            }
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                System.out.println("Error closing Connection.");
                e.printStackTrace();
            }
        }
    }
}

Java 7+

private static final String INSERT_STATEMENT =
        "INSERT INTO at_accounts (acc_email_address, acc_password, "
      + "acc_enabled) VALUES (?, ?, ?)";

public void createUser(String userName, String pass) {
    String pw_hash = BCrypt.hashpw(pass, BCrypt.gensalt());

    // NOTE: See Braj's answer for a better way of getting Connections.
    try (Connection conn = DriverManager.getConnection(
            "jdbc:mysql://localhost/awardtracker", "awtrack",
            "**************");
         PreparedStatement ps = conn.prepareStatement(INSERT_STATEMENT);) {

        try {
            ps.setString(1, userName);
            ps.setString(2, pw_hash);
            ps.setString(3, "1");
            ps.executeUpdate();
            conn.commit();
        } catch (SQLException e) {
            try {
                conn.rollback();
            } catch (SQLException e2) {
                System.out.println("Error rolling back transaction.");
                e2.printStackTrace();
            }
            System.out.println("SQLException createUser 1.");
            e.printStackTrace();
        }
    } catch (SQLException e) {
        System.out.println("Error connecting to DB.");
        e.printStackTrace();
    }
}

在这两个示例中,我都删除了未使用的方法参数(如果你什么都不做,为什么还要在那里?)并将返回类型更改为void。我这样做是因为在当前形式下,您的方法将始终 return null(您将您的 User 对象初始化为 null,然后不对其进行任何操作以更改其值,然后在结束)。

您还应该考虑使用诸如log4j 之类的日志记录框架来处理您的异常日志记录,而不是依赖printStackTrace()。请参阅Why is exception.printStackTrace() considered bad practice?,了解有关为什么不推荐使用printStackTrace() 的更多信息。

【讨论】:

  • 您好JoinK,谢谢。我在上面输入了修改后的代码。这是正确的/你的意思吗?问候,格林。
  • 嗨 JonK,我已经用你的错误处理更新了我上面的代码。如果这没问题,那么我将按照 Braj 的建议将所有错误处理移动到一个类中。然后,我将研究使用 log4j 并将连接移动到 XML。问候,格林。
  • 您好 JonK,在测试此用户时,成功创建了用户,但是会生成错误,当自动提交为 yes 时,您无法提交。所以我删除了“conn.commit();”线。这是正确的做法还是我应该关闭自动提交(这是在哪里)?问候,格林。
  • Glyn - 你应该关闭自动提交是的。 This article 告诉你如何关闭它。
  • 好的,感谢您的帮助。我认为我所做的其余部分是正确的。问候,格林。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 2016-05-21
  • 2020-01-29
相关资源
最近更新 更多