良好做法:JDBC连接


问题内容

我已经为JDBC连接编写了一个简单的包装程序,它可以工作,但是我想通过最佳实践来对其进行改进。它基本上有类似的方法open()close()isOpened()select()insert()update()delete()batch()。为简单起见,我仅在此处发布前4种方法。

public class Query{
    private Connection con;
    private PreparedStatement ps;
    private ResultSet rs;

    //Database.open() returns a Connection ready to use
    public void open (Database database) throws DatabaseException, SQLException{
        if (!isOpened ()){
            con = database.open ();
        }
    }

    public void close () throws SQLException{
        if (isOpened ()){
            if (ps != null) ps.close ();
            con.close ();
            con = null;
        }
    }

    public boolean isOpened (){
        return con != null;
    }

    //The query string is the query without the word "select" and can use placeholders (?)
    //The args param it's just an array owith the values of this placeholders
    public ResultSet select (String query, Object[] args) throws SQLException{
        if (ps != null) ps.close ();

        if (isOpened ()){
            ps = con.prepareStatement ("select " + query);
            if (args != null){
                for (int i=0; i<args.length; i++){
                    ps.setObject (i+1, args[i]);
                }
            }
            rs = ps.executeQuery ();
        }

        return rs;
    }
}

笔记:

  • 可以重复使用同一查询对象,例如打开和关闭它,以及在再次打开之后。
  • 我不是关闭每个查询的连接,而是关闭准备好的语句(这是正确的,或者我可以让准备好的语句保持打开状态,因为Connection对象会关闭它吗?)
  • 当我关闭时Connection,所有PreparedStatements和它们的ResultSets也都关闭了,对吗?

用法:

Database database;

//Database initialization

Query query = new Query ();


query.open (database);

ResultSet rs = query.select ("* from user where name=?", new String[]{ "MyName" });
doSomethingWithResult1 (rs);

//Connection is not closed here

ResultSet rs = query.select ("coordx from point where coordy=? and coordz=?", new Float[]{ 0.1, 0.2 });
doSomethingWithResult2 (rs);

query.close ();


query.open (database);

ResultSet rs = query.select ("* from user where name=?", new String[]{ "MyName" });
doSomethingWithResult1 (rs);

//Connection is not closed here

ResultSet rs = query.select ("coordx from point where coordy=? and coordz=?", new Float[]{ 0.1, 0.2 });
doSomethingWithResult2 (rs);

query.close ();

你怎么看?每次查询后都应该关闭并打开连接吗?在同一连接上进行每个查询后,是否可以打开PreparedStatement?这是一个好的设计?


问题答案:

使用完PreparedStatement之后,必须在同一连接上创建新的PreparedStatement之后,再关闭它。我遇到了严重的问题,因为我没有关闭PreparedStatements。事实证明,在数据库服务器上,只有在显式调用PreparedStatement.close()之后才释放已分配的资源。

如bdares所评论,应尽可能不频繁地打开和关闭Connection。