创建preparedstatement的多个实例

Posted

技术标签:

【中文标题】创建preparedstatement的多个实例【英文标题】:Creating multiple instances of preparedstatement 【发布时间】:2016-03-11 10:27:57 【问题描述】:

我是 jdbc 编程的新手。我正在多次创建PreparedStatement 的实例并将其分配给相同的引用变量。在创建PreparedStatement 的第二个实例之前,我是否需要关闭PreparedStatement 的第一个实例?

oPrepStmt = oRoot.con.prepareStatement(strSql);

if (strProviderType.length() > 0) 

        strSql += " and users.usertype IN (?)";

        // DO I need to close prepare statement, before creating another instance of preparestatement and assigning to same reference variable.
        // i.e. oPrepStmt.close();
        oPrepStmt = oRoot.con.prepareStatement(strSql);
        oPrepStmt.setString(2,strProviderType);


oPrepStmt.setInt(1,oRoot.getTrUserId());

preparedstatement 未关闭的第一个实例是否会导致资源泄漏?

【问题讨论】:

你为什么要这样?为什么不先构造完整的查询字符串,然后准备语句? 如您所见,我需要动态创建查询。 "preparedstatement 的未关闭的第一个实例是否会导致资源泄漏" - 是的(尽管它在一定程度上取决于 DBMS 和 JDBC 驱动程序)。 你能告诉我更多关于这个的细节吗?此外,您能建议编写此类代码的最佳方法吗? 可以先确定需要的sql,准备好语句,再确定是否需要设置参数。多次准备一条语句的开销高于使用两条 if 语句而不是一条。 【参考方案1】:

JDBC 语句实现 AutoCloseable,因此表明在不再需要时应显式关闭。

可能持有资源的对象(例如文件或套接字句柄) 直到关闭。 AutoCloseable 对象的 close() 方法是 退出 try-with-resources 块时自动调用 该对象已在资源规范标头中声明。 这种构造保证了及时释放,避免了资源枯竭 否则可能发生的异常和错误。

所以按照 Javadoc 的建议,使用 try-with-resources 语句:

try (PreparedStatement pstmt = oRoot.con.prepareStatement(strSql)) 
     ... run sql commands ...

在您的示例中,您创建了一个语句并在某些情况下将其丢弃。最好避免这种情况并写成这样:

boolean checkUserType = strProviderType.length();

try (PreparedStatement pstmt = oRoot.con.prepareStatement(checkUserType ? strSql : strSql + " and users.usertype IN (?)") 
    oPrepStmt.setInt(1,oRoot.getTrUserId());
    if (checkUserType)
        oPrepStmt.setString(2,strProviderType);
    ...

【讨论】:

【参考方案2】:

当你完成一个语句时,你应该总是关闭它。在某些数据库/JDBC 驱动程序中,语句还具有服务器端句柄。不关闭语句将使该句柄在服务器上保持打开状态,从而导致不必要的资源消耗(主要是内存,但它可能会锁定某些元数据对象)。

除此之外,在驱动程序端不关闭语句还可能有额外的资源消耗(内存、连接事件的侦听器等)。因此建议尽快关闭。

驱动程序最终可能会通过终结器或在您关闭连接时解除分配,但依赖它并不是一个好主意(例如,连接池中的连接并不总是正确关闭语句时返回到池中等)。

现在关于您的具体问题,您应该将代码修改为:

if (strProviderType.length() > 0) 
    strSql += " and users.usertype IN (?)";


try (PreparedStatement oPrepStmt = oRoot.con.prepareStatement(strSql)) 
    oPrepStmt.setInt(1,oRoot.getTrUserId());
    if (strProviderType.length() > 0) 
        oPrepStmt.setString(2, strProviderType);
    

    oPrepStmt.execute(); // or executeQuery or executeUpdate

我还包含了一个 try-with-resources 以确保语句尽快关闭。

顺便说一句,您对IN(?) 的使用可能不会在大多数(所有?)数据库上像这样工作。见PreparedStatement IN clause alternatives?

【讨论】:

以上是关于创建preparedstatement的多个实例的主要内容,如果未能解决你的问题,请参考以下文章

Statement与PreparedStatement的区别

将参数传递给 JDBC PreparedStatement

PreparedStatement 更新显示错误 ORA-00927 缺少等号

JDBC statement的常用方法

Statement及PreparedStatement执行多个sql

CentOS6.5源码安装多个MySQL实例及复制搭建