【问题标题】:Where to create a prepared statement with JDBC?在哪里使用 JDBC 创建准备好的语句?
【发布时间】:2010-05-12 09:31:46
【问题描述】:

考虑以下方法,从某些数据结构 (InteractionNetwork) 读取数据并使用 SQLite-JDBC dirver 将它们写入 SQLite 数据库中的表:

private void loadAnnotations(InteractionNetwork network) throws SQLException {
    PreparedStatement insertAnnotationsQuery = 
        connection.prepareStatement(
        "INSERT INTO Annotations(GOId, ProteinId, OnthologyId) VALUES(?, ?, ?)");
    PreparedStatement getProteinIdQuery = 
        connection.prepareStatement(
        "SELECT Id FROM Proteins WHERE PrimaryUniProtKBAccessionNumber = ?");
    connection.setAutoCommit(false);
    for(common.Protein protein : network.get_protein_vector()) {
        /* Get ProteinId for the current protein from another table and
           insert the value into the prepared statement. */
        getProteinIdQuery.setString(1, protein.get_primary_id());
        ResultSet result = getProteinIdQuery.executeQuery();
        result.next();
        insertAnnotationsQuery.setLong(2, result.getLong(1));
        /* Extract all the other data and add all the tuples to the batch. */
    }
    insertAnnotationsQuery.executeBatch();
    connection.commit();
    connection.setAutoCommit(true);
}

这段代码运行良好,程序运行时间约为 30 秒,平均占用 80m 堆空间。因为代码看起来很难看,我想重构它。我做的第一件事是将getProteinIdQuery 的声明移到循环中:

private void loadAnnotations(InteractionNetwork network) throws SQLException {
    PreparedStatement insertAnnotationsQuery = 
        connection.prepareStatement(
        "INSERT INTO Annotations(GOId, ProteinId, OnthologyId) VALUES(?, ?, ?)");
    connection.setAutoCommit(false);
    for(common.Protein protein : network.get_protein_vector()) {
        /* Get ProteinId for the current protein from another table and
           insert the value into the prepared statement. */
        PreparedStatement getProteinIdQuery = // <--- moved declaration of statement here
            connection.prepareStatement(
            "SELECT Id FROM Proteins WHERE PrimaryUniProtKBAccessionNumber = ?");
        getProteinIdQuery.setString(1, protein.get_primary_id());
        ResultSet result = getProteinIdQuery.executeQuery();
        result.next();
        insertAnnotationsQuery.setLong(2, result.getLong(1));
        /* Extract all the other data and add all the tuples to the batch. */
    }
    insertAnnotationsQuery.executeBatch();
    connection.commit();
    connection.setAutoCommit(true);
}

当我现在运行代码时会发生什么,它需要大约 130m 的堆空间并且需要很长时间才能运行。谁能解释这种奇怪的行为?

【问题讨论】:

    标签: java sqlite jdbc transactions prepared-statement


    【解决方案1】:

    正如您所发现的,准备一份声明需要时间。不管代码丑不丑,速度下降也很丑,所以你需要使用更快的形式。

    但你可以做的是使用内部类来保存细节并提供更好的界面:

    private class DatabaseInterface {
        private PreparedStatement insertAnnotation, getProteinId;
        public DatabaseInterface() {
            // This is an inner class; 'connection' is variable in outer class
            insertAnnotation = connection.prepareStatement(
                "INSERT INTO Annotations(GOId, ProteinId, OnthologyId) VALUES(?, ?, ?)");
            getProteinId = connection.prepareStatement(
                "SELECT Id FROM Proteins WHERE PrimaryUniProtKBAccessionNumber = ?");
        }
        public long getId(Protein protein) { // Exceptions omitted...
            getProteinId.setString(1, protein.get_primary_id());
            ResultSet result = getProteinId.executeQuery();
            try {
                result.next();
                return result.getLong(1);
            } finally {
                result.close();
            }
        }
        public void insertAnnotation(int GOId, long proteinId, String ontologyId) {
            insertAnnotation.setInt(1, GOId);          // type may be wrong
            insertAnnotation.setLong(2, proteinId);
            insertAnnotation.setString(3, ontologyId); // type may be wrong
            insertAnnotation.executeUpdate();
        }
    }
    private void loadAnnotations(InteractionNetwork network) throws SQLException {
        connection.setAutoCommit(false);
        DatabaseInterface dbi = new DatabaseInterface();
        for(common.Protein protein : network.get_protein_vector()) {
            dbi.insertAnnotation(..., dbi.getId(protein), ...);
        }
        connection.commit();
        connection.setAutoCommit(true);
    }
    

    目标是您拥有一段代码知道如何将事物整合到 SQL 中(如果您使用不同的数据库,这很容易适应),而另一段代码知道如何将这些事物协调在一起。

    【讨论】:

    • 还请注意,您可以考虑更广泛地共享胶水类;它与连接相关,而不是特定操作。
    【解决方案2】:

    如果第一个片段看起来很难看,我想这是一个品味问题 ;-)...

    但是,第二个代码片段需要更长的时间(恕我直言)的原因是,现在对于 for 循环的每次迭代,都会创建一个 PreparedStatement (getProteinIdQuery) 的新实例,而在第一个片段中,您重用了准备好的语句,以应有的方式使用它:实例化,然后提供适当的值。

    至少,这是我的看法... 一月

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-12-13
      • 1970-01-01
      • 1970-01-01
      • 2012-08-28
      • 1970-01-01
      • 2017-04-12
      • 2013-12-06
      相关资源
      最近更新 更多