Java 为什么我需要一个连接来创建PreparedStatements?

Java 为什么我需要一个连接来创建PreparedStatements?,java,sql,jdbc,prepared-statement,Java,Sql,Jdbc,Prepared Statement,我想使用事先准备好的声明,例如。 但是,我想创建一个如下所示的方法: /* This opens a connection, executes the query, and closes the connection */ public static void executeNonQuery(String queryString); SELECT * FROM table WHERE col1 = ? AND col2 = ? 换句话说,我希望我的应用程序逻辑只需

我想使用事先准备好的声明,例如。 但是,我想创建一个如下所示的方法:

/* This opens a connection, executes the query, and closes the connection */
public static void executeNonQuery(String queryString);
SELECT  *
FROM    table
WHERE   col1 = ?
        AND col2 = ?
换句话说,我希望我的应用程序逻辑只需要制定查询和输入参数,而不需要处理连接和语句。但是,PreparedStatements是从连接对象创建的,因此我目前被迫使用string.format()-来准备查询字符串

有没有一种方法可以不使用String.format()完成我想要的操作

为什么我需要一个连接来创建PreparedStatements

因为在大多数
RDBMS
中,这些语句是基于每个连接准备的

准备好的语句实际上是缓存的执行计划,它不考虑您的权限、编码、排序规则设置等

所有这些都是在查询解析期间完成的

有没有办法不用
String.format()

不明白为什么需要在此处使用
String.format()

您可以将查询实现为类,创建连接,在类构造函数中准备查询,然后在方法中执行查询

参数化查询通常如下所示:

/* This opens a connection, executes the query, and closes the connection */
public static void executeNonQuery(String queryString);
SELECT  *
FROM    table
WHERE   col1 = ?
        AND col2 = ?
,其中,在查询执行期间,绑定参数将替换为

如果需要
静态方法

  • 创建一个
    静态
    连接句柄
  • 使用参数化查询文本作为
    ,并将准备好的查询的句柄作为
    ,创建准备好的查询的
    静态
    哈希表
  • 无论何时要执行查询,都要找到其句柄(如果找不到,则创建它),并使用来绑定参数并执行查询
为什么不让您的“应用程序”逻辑使用您创建的数据层,它可以呈现这种接口方法

然后,您的数据层可以处理创建连接、准备语句等,所有这些都在
executeNonQuery
方法中

我认为,如果您试图自己将查询/语句中的参数合并成一个字符串,那么您就是自食其果,实际上没有使用PreparedStatements的参数功能。不知道你为什么要这样做


您可能还需要考虑使用一个API,比如Spring,它有一系列的
JdbcTemplate
类,这些类可以将所有连接处理抽象出来,但是仍然允许您使用
映射中的参数

我通过使用一个名为QueryRunner的类来提取所有JDBC内容,该类具有一个接受sql的执行方法、一个表示参数的对象列表和一个将处理结果集的对象。如果使用JDBC中的setObject方法来设置参数,它将根据底层对象找出要使用的适当DB类型。这是我代码的一部分。我有另一个方法,它把这个包起来,得到的是连接

public void executeNoCommit(Connection conn,
                            String sql, 
                            List params, 
                            ResultSetProcessor processor) throws SQLException {
    PreparedStatement stmt = null;
    ResultSet rs = null;
    int updateCount = 0;
    Iterator it;
    int paramIndex = 1;
    boolean query;

    try {
        stmt = conn.prepareStatement(sql);

        if (params != null) {
            it = params.iterator();
            while (it.hasNext()) {
                stmt.setObject(paramIndex, it.next());
                paramIndex++;
            }
        }

        query = stmt.execute();
        if (query) {
            rs = stmt.getResultSet();
        }
        else {
            updateCount = stmt.getUpdateCount();
        }

        processor.process(rs, updateCount);
    }
    finally {
        if (rs != null) {
            try {
                rs.close();
            }
            catch (SQLException e) {
                log.error(e);
            }
        }

        if (stmt != null) {
            try {
                stmt.close();
            }
            catch (SQLException e) {
                log.error(e);
            }
        }
    }
}

在Apache Commons库中,您可能需要类似于DbUtils包的东西:[

QueryRunner类允许您执行sql语句,而无需手动创建PreparedStatements,甚至无需为此打开连接。从示例页面:

QueryRunner run = new QueryRunner( dataSource );
try
{
    // Create an object array to hold the values to insert
    Object[] insertParams = {"John Doe", new Double( 1.82 )};
    // Execute the SQL update statement and return the number of
    // inserts that were made
    int inserts = run.update( "INSERT INTO Person (name,height) VALUES (?,?)",
                              insertParams );

    // Now it's time to rise to the occation...
    Object[] updateParams = {new Double( 2.05 ), "John Doe"};
    int updates = run.update( "UPDATE Person SET height=? WHERE name=?",
                              updateParams );
}
catch(SQLException sqle) {
    // Handle it
}

因此,它基本上透明地处理准备语句的创建,您真正需要知道的唯一一件事是数据源。这也适用于非更新/插入语句,即普通的select查询,并且创建ResultSetHandler的功能使您能够将ResultSet转换为类似ful的内容准备得很好的bean,或者是一个映射,其中键是列名,值是实际的行值。对于无法实现整个ORM解决方案的情况非常有用。

ExecuteOnQuery方法有一个问题:获取连接。如果每次执行此方法时都创建一个连接,则在一段时间内执行时会出现性能问题d over(创建和关闭连接非常昂贵)。如果将其封装在仅在第一次调用时创建连接的对象上,则会出现问题:何时关闭?可能基于时间?如果使用静态字段进行缓存,则不会收集到该数据。但是,在缓存连接时,请注意并发调用:没有锁定机制(例如syncrhonized),您可以创建许多连接。