【问题标题】:SQL connections dangling: Where am I not correctly closing up connections correctly?SQL 连接悬空:我在哪里没有正确关闭连接?
【发布时间】:2019-06-14 14:54:26
【问题描述】:

我正在构建一个基本的 java 应用程序来将一些文件加载​​到 mysql 数据库中。我能够毫无问题地加载文件并填充我的表格。然而,在与查看我的代码的人交谈后,我显然没有正确关闭我的连接并浪费资源。我在哪里没有关闭连接?我做错了吗?

我正在使用我的 DbSinger 类中的 try-with-resources 构造来对我的数据库执行准备好的语句,只要实现了 AutoCloseable 接口,它就会自动关闭连接,它就是在 Db 的父类中。然而,close() 方法永远不会到达。 DbSinger 在我的 main() 中实例化,然后运行它的单一方法 populateSingers()Singer的 ArrayList > 对象。

连接类

public class SQLConnection {
    private static final String servername = "localhost";
    private static final int port = 3306;
    private static final String user = "ng_user";
    private static final String pass = "ng";
    private static final String db = "ng_music";
    private static final String connectionString = "jdbc:mysql://" + servername + ":" + port + "/" + db;

    public Connection provide() {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");

            return DriverManager.getConnection(connectionString, user, pass);

        }
        catch (SQLException | ClassNotFoundException e) {
            throw new SQLConnectionException(e);
        }
    }

    public class SQLConnectionException extends RuntimeException {
        SQLConnectionException(Exception e) {super(e);}
    }
}

抽象父类

public abstract class Db implements AutoCloseable{
    private Connection connection;

    Db() {
        SQLConnection sqlC = new SQLConnection();
        this.connection = sqlC.provide();
    }

    @Override
    public synchronized void close() throws SQLException {
        if(connection != null) {
            connection.close();
            connection = null;
            System.out.println("Connection closed");
        }
    }
    Connection getConnection() {
        return connection;

    }
    boolean checkIfPopulated(String query){
        try {
            PreparedStatement ps = getConnection().prepareStatement(query);
            ResultSet rs = ps.executeQuery();
            return !rs.next();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return true;
    }
}

对歌手表的数据库执行查询的具体类

public class DbSinger extends Db {
    public DbSinger() {
        super();
    }

    public void populateSingers(ArrayList<Singer> singers) {
        String populateSingersQuery = "insert into ng_singers(name, dob, sex) values(?,?,?)";
        if(!checkIfPopulated("select * from ng_singers")){
            System.out.println("Singer Table is already populated");
            return;
        }
        try (PreparedStatement ps = getConnection().prepareStatement(populateSingersQuery)) {
            for (Singer s : singers) {
                ps.setString(1, s.getName());
                ps.setDate(2, java.sql.Date.valueOf(s.getDob()));
                ps.setString(3, s.getSex());
                ps.addBatch();
            }
            ps.executeBatch();
            System.out.println("Singers added to table");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

我的代码能够执行,能够正常运行并执行所需的操作,但我想了解为什么以及在何处不关闭连接,并了解如何解决此问题。或者至少了解我是否接近这个错误。

【问题讨论】:

  • 请出示实例化DbSinger的代码。另外,您没有在checkIfPopulated 方法中关闭PreparedStatement
  • 没问题! public static void main(String[] args) { start(); } public static void start() { DAO dao = new DAO(); DbSinger dbSinger = new DbSinger(); dbSinger.populateSingers(dao.loadSingers("data/ng_singers.txt")); } DAO 类和 dao.loadSingers 只返回 Singer 对象的 ArrayList。我认为通过使用 try-with-resources 我不需要每次都关闭连接?但如果不是这样,我会手动关闭它们
  • 将该代码放入您的问题中并查看我的答案。

标签: java mysql connection try-with-resources


【解决方案1】:

在您的情况下,您需要在try-with-resources 语句中实例化 DBSinger 类以关闭底层连接。

而不是做:

DbSinger dbSinger = new DbSinger();

你需要做的:

try (DbSinger dbSinger = new DbSinger()) {
// Your other code
}

这样,您在 Db 类中覆盖的 close() 方法将被自动调用。

另外,通过以下方式关闭您在checkIfPopulated 方法中创建的preparedStatement

try (PreparedStatement ps = getConnection().prepareStatement(query)) {
// Other codes
}

【讨论】:

  • 啊,我现在明白了!谢谢你的澄清!
【解决方案2】:

您的代码是旧方法。而且您确实需要手动关闭。但是,在 Java 8 中,您可以使用 try with resource,如下所示,

 try (Connection conn = ds.getConnection();
    Statement stmt = conn.createStatement()) {
    try {
       stmt.execute(dropsql);
   } catch (Exception ignore) {} // ignore if table not dropped
   stmt.execute(createsql);
   stmt.execute(insertsql);
   try (ResultSet rs = stmt.executeQuery(selectsql)) {
     rs.next();
   } catch (Exception e2) {
     e2.printStackTrace();
     return("failed");
   }
 } catch(Exception e) {
   e.printStackTrace();
   return("failed");
 }

【讨论】:

  • 感谢您的回答!如果我不太理解,请原谅我,但我看不出你的例子有什么不同?在try 中,您正在获取连接,然后从该连接创建一个语句,然后在正文中执行它并捕获任何异常。我的代码正在做大致相同的事情,期望连接已经在父类中创建。这是在每次尝试资源中创建新连接的最佳方式吗?
  • 没有。不同的是它会自动为你关闭资源。
猜你喜欢
  • 2011-06-29
  • 1970-01-01
  • 2016-08-08
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-20
相关资源
最近更新 更多