Где создать готовое заявление с JDBC? - PullRequest
1 голос
/ 12 мая 2010

Рассмотрим следующий метод, который считывает данные из некоторой структуры данных (InteractionNetwork) и записывает их в таблицу в базе данных SQLite, используя SQLite-JDBC dirver :

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 секунд и занимает в среднем 80 м пространства кучи. Поскольку код выглядит некрасиво, я хочу его реорганизовать. Первое, что я сделал, это переместил объявление 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);
}

Что происходит, когда я запускаю код сейчас, так это то, что он занимает около 130 м пространства кучи и требует вечности для запуска. Кто-нибудь может объяснить это странное поведение?

Ответы [ 2 ]

2 голосов
/ 12 мая 2010

Подготовка заявления требует времени, как вы узнали. Независимо от того, уродлив код или нет, это снижение скорости тоже очень уродливо, поэтому вам нужно использовать более быструю форму.

Но то, что вы могли бы сделать, это использовать внутренний класс для хранения деталей и предоставить более приятный интерфейс:

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 голосов
/ 12 мая 2010

Полагаю, это дело вкуса, если первый фрагмент выглядит безобразно; -) ...

Однако причина, по которой второй фрагмент кода занимает больше времени (IMHO), заключается в том, что теперь для каждой итерации цикла for создается новый экземпляр PreparedStatement (getProteinIdQuery), тогда как в первом фрагменте вы повторно использовали подготовленный оператор, используя его таким, каким он должен был быть: создан и затем снабжен правильными значениями.

По крайней мере, это мое мнение ... Jan

...