【问题标题】:Is it ok to use same BasicDataSource, Connection, Statement and ResultSet Object in multiple class methods.?可以在多个类方法中使用相同的 BasicDataSource、Connection、Statement 和 ResultSet 对象吗?
【发布时间】:2019-03-06 12:36:22
【问题描述】:

下面的代码使用了 BasicDataSource、Sql Connection、Statement 和 ResultSet 的静态对象。下面的代码运行良好,但我只想知道使用这些编码实践的安全性。或者我该如何优化下面的代码,使其变得更加稳定和可靠。

public class Testing {
     static BasicDataSource bds = DBConnection.getInstance().getBds();
     static Connection con = null;
     static PreparedStatement stmt = null;
     static ResultSet rs = null;

    private void show() {
        try {
            con = bds.getConnection();
            stmt = con.prepareStatement("SELECT * FROM users");
            rs = stmt.executeQuery();
            if(rs.next()) {
                System.out.println(rs.getString("firstname") + " " + rs.getString("lastname"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    private void display() {
        try {
            con = bds.getConnection();
            stmt = con.prepareStatement("SELECT * FROM agent_cities");
            rs = stmt.executeQuery();
            while(rs.next()) {
                System.out.println(rs.getString("city_name"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    private void add() {
        try {
            con = bds.getConnection();
            stmt = con.prepareStatement("UPDATE users SET firstname = 'shsh' WHERE id = 2");
            stmt.executeUpdate();
            System.out.println("updated successfully");
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        Testing t = new Testing();
        t.show();
        t.display();
        t.add();
    }
}

提前致谢。 请分享您可以破解上述代码并质疑其安全性的案例。

更新: 更新只是为了确保没有人应该使用我在上述程序中使用的静态字段,因为上述程序在部署到开发服务器时包含错误。

在大型系统上使用上述代码后,我发现了这个错误。一个月前我对上面的代码没有任何问题,它工作正常,但今天我发现了这个错误。

错误:

点击我的 API 6-7 次后,它在第 8 次点击时停止响应。我真的不知道为什么,也不知道程序中存在循环漏洞。 但现在我已经接受了答案,我更改了我的源代码并开始在我的代码中使用 try-with 资源并删除了静态字段。

但我仍然很想知道我在上述代码中发现的错误。 在 7-8 API 命中后没有响应并挂起。 请分享您对此的看法。我正在使用 apache tomcat 8.5.32 服务器。 提前致谢。

【问题讨论】:

  • 根据应用程序的复杂性,您确实不应该将字段用于连接、语句和结果集(静态字段的味道更大)。
  • 那么有什么更好的选择。
  • 当然是局部变量。
  • 这就是原因
  • 防止连接过长,防止无意间在多个线程之间共享连接,防止其他类型的资源泄漏。

标签: java maven debugging garbage-collection apache-commons-dbcp


【解决方案1】:

更好地使用 try-with-resources。这会自动关闭 Connection、Statement 和 ResultSet,即使引发异常或内部返回也是如此。

    String sql = "UPDATE users SET firstname = ? WHERE id = ?";
    try (Connection con = bds.getConnection();
            PreparedStatement stmt = con.prepareStatement()) {
        stmt.setString(1, "shsh");
        stmt.setLong(2, 2);
        stmt.executeUpdate();
        System.out.println("updated successfully");
    }

    String sql = "SELECT city_name FROM agent_cities";
    try (Connection con = bds.getConnection();
            PreparedStatement stmt = con.prepareStatement()) {
        try (ResultSet rs = stmt.executeQuery()) {
            while(rs.next()) {
                System.out.println(rs.getString("city_name"));
            }
        }
    }

这更适合垃圾收集。防止不美观的 rs2、rs3。允许多用户并发,例如在服务器应用程序中。调用自己的查询。 而static 更是全局变量的风格。

【讨论】:

  • 这不是关于垃圾收集,而是关于资源清理,而“资源”是指由 Java 的垃圾收集器管理的任何资源。 Java 堆中这些对象的内存只是一个小问题。
  • @Holger 是的,谢谢。事实上,我有点迷失在这里找到正确的单词。旧式 C / Fortran 程序员会按照建议使用全局变量。
  • 措辞很棘手,与其他编程语言一样,任何需要分配和释放的东西都是“资源”,其中包括显式管理的内存。 Java 程序员的头脑工作是从资源集中排除托管堆内存,同时将“资源”视为仍然需要显式关闭的东西(考虑使用try(…) 作为显式管理)并且应该尽早关闭.然后,就更容易理解在终结器中关闭资源的旧想法是徒劳的,因为垃圾收集器关心堆内存。
  • 谢谢。从现在开始,我将使用上面的 try-with-resources 块。
  • @JoopEggen 请查看有问题的更新部分并分享您的意见
【解决方案2】:

如果我们谈论这样一个小程序,或多或少是可以的。 但是没有必要将 con、stmt 和 rs 保留为静态变量,它们可以在方法内部声明。另外,你需要重写try catch finally块并正确关闭资源:

Connection con = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
  // your code
} catch (SQLException e) {
  e.printStackTrace();
} finally {
  try { if (rs != null) rs.close(); } catch (Exception e) {e.printStackTrace();}
  try { if (stmt != null) stmt.close(); } catch (Exception e) {e.printStackTrace();}
  try { if (conn != null) conn.close(); } catch (Exception e) {e.printStackTrace();}
}

下一步,您可以检查try-with-resources 构造以清理此代码。

【讨论】:

  • 是的。但是我有很多大型程序,我无法为每个方法签名创建新对象,那么对于包含许多方法签名的大型程序,您的解决方案是什么。
  • 这取决于技术栈。如果你有没有spring/hibernate的java,那么你需要一个数据源(你已经有static BasicDataSource bds = DBConnection.getInstance().getBds();)并正确关闭资源。
  • 是的,我没有使用 spring/hibernate。我使用 Java Servlets 和 Maven 作为 Eclipse IDE 的构建工具。
【解决方案3】:

您应该使用 try-with-resources 来避免任何类型的连接泄漏。

以下示例从文件中读取第一行。它使用 BufferedReader 的实例从文件中读取数据。 BufferedReader 是一个资源,程序用完后必须关闭:

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br =
                   new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

【讨论】:

    猜你喜欢
    • 2013-07-05
    • 2011-08-01
    • 2010-11-05
    • 2015-11-21
    • 2014-05-05
    • 1970-01-01
    • 2016-09-03
    • 2012-07-17
    相关资源
    最近更新 更多