【问题标题】:Proper way to handle SQL database connections in java OOP style以 java OOP 风格处理 SQL 数据库连接的正确方法
【发布时间】:2016-11-07 10:32:07
【问题描述】:

我最近参加了一个编程挑战。公开一个用于管理虚构电影租赁公司的 API 是一个 OOP 挑战。我选择使用 sparkjava,并使用 heroku 模板。 I have posted the code to Github here,并邀请任何人查询 README 文档中提供的 URL。密码是secret

我收到了很多关于需要改进的内容的良好反馈。一种反对意见是:

直接通过驱动程序处理连接,无需任何处理 正确关闭连接。

所以我想弄清楚这意味着什么,为什么会出现问题,以及如何解决它并使我的代码更好。

比如我有这个方法,in the main class:

public static int validate_customer(String cust) throws SQLException, URISyntaxException {
    int customer = 0;
    try{
        customer = Integer.parseInt(cust);
    }catch (Exception e){
        throw new SQLException("Invalid customer integer -> " + cust);
    }

    Connection connection = DatabaseUrl.extract().getConnection();
    PreparedStatement stmt = connection.prepareStatement("SELECT count(*) from customers where id = ?;");
    stmt.setInt(1, customer);
    ResultSet rs = stmt.executeQuery();
    rs.next();
    if ( rs.getInt(1) < 1 ){
        throw new SQLException("Invalid customer id -> " + customer);
    }
    rs.close();
    stmt.close();
    return customer;
}

为什么这是处理与数据库交互的错误方式?主类中还有其他与数据库交互的方法,但技术与本示例基本相同。

对我来说,如果与数据库的连接出现问题,它会出现在以下行:Connection connection = DatabaseUrl.extract().getConnection(); 并抛出 SQLException。如果连接发生问题,并且查询的某些部分不起作用,则此validate_customer 方法将引发异常。为什么这是我的代码中的问题?

【问题讨论】:

  • 一个问题是您要么在不再需要连接时不关闭连接(例如,在出现异常的情况下,尤其是在连接本身断开的情况下),要么您可能过早关闭它(例如,如果您在该方法中调用connection.close())。
  • @Thomas - 感谢您的帮助。使用连接池会解决这个问题吗?

标签: java mysql oop heroku


【解决方案1】:

我经常看到在 try catch 的 finally 块中关闭连接。也许在 try catch 语句中处理方法内部的 SQL 异常会更好,并在 finally 块中关闭资源。这正是我经常看到的。我希望它可能有所帮助。


为了更新这一点,Java 7 引入了一个名为“try-with-resources”的功能。 这似乎是清理资源的新首选方式。 举个例子:

try(Connection connection = Connection.getConnection();
    PreparedStatement stmt = connection.prepareStatement("..."))
{
    ...
}

使用资源的 try 将自动关闭所有实现 AutoClosable 接口并在 try 语句的括号中声明的资源。

这里是官方教程文档的链接:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

【讨论】:

    【解决方案2】:

    我建议您使用datasource。在你的类路径中有数据源和 jdbc 数据库特定驱动程序的依赖项。在这种情况下,我使用dbcp2mysql

    public long count(final int customerId) {
    
        int count;
    
        final String query = "SELECT count(*) FROM customers WHERE id = ?";
    
        try (final Connection conn = dataSource.getConnection();
             final PreparedStatement prepStatement = conn.prepareStatement(query)) {
    
            prepStatement.setInt(1, customerId);
    
            final ResultSet rs = prepStatement.executeQuery();
    
            rs.next();
            count = rs.getInt(1);
    
        } catch (SQLException e) {
    
            // You may want to log and throw a custom exception here.
            throw new RuntimeException(e);
        }
    
        return count;
    }
    
    public DataSource dataSource() {
    
        final org.apache.commons.dbcp2.BasicDataSource dataSource =
            new org.apache.commons.dbcp2.BasicDataSource();
    
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl(environment.getProperty("jdbc:mysql://localhost:3306/myDatabase"));
        dataSource.setUsername(environment.getProperty("root"));
        dataSource.setPassword(environment.getProperty("password"));
    
        return dataSource;
    }
    

    请注意,我没有明确关闭 finally 块上的连接,因为从 Java 7 开始,ConnectionPreparedStatementauto-closeable resources .

    【讨论】:

      【解决方案3】:

      直接通过驱动处理连接

      这意味着您没有使用数据库连接池(如HikariCP),这意味着您的连接限制可能会很快耗尽。 Heroku 示例没有使用 DB 连接池来限制示例中的依赖关系。

      没有任何正确关闭连接的处理。

      这意味着stmt.close(); 不在finally 块中,这意味着如果在方法中抛出异常,stmt 将永远不会关闭。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多