Возврат ResultSet

java mysql jdbc

75149 просмотра

6 ответа

6122 Репутация автора

Я пытаюсь создать метод, из которого я могу запросить свою базу данных и получить всю таблицу.

В настоящее время все работает нормально, если я использую данные внутри метода. Однако я хочу, чтобы метод возвращал результаты.

Я получаю java.sql.SQLException: Operation not allowed after ResultSet closedна текущий код.

Как мне этого добиться?

public ResultSet select() {

    con = null;
    st = null;
    rs = null;

    try {
        con = DriverManager.getConnection(url, user, password);
        st = con.createStatement();

        rs = st.executeQuery("SELECT * FROM biler");
        /*
        if (rs.next()) {
            System.out.println(rs.getString("model"));
        }*/

    } catch (SQLException ex) {
        Logger lgr = Logger.getLogger(MySQL.class.getName());
        lgr.log(Level.SEVERE, ex.getMessage(), ex);

    } finally {
        try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (con != null) {
                con.close();
            }

        } catch (SQLException ex) {
            Logger lgr = Logger.getLogger(MySQL.class.getName());
            lgr.log(Level.WARNING, ex.getMessage(), ex);
        }
    }

    return rs;
}
Автор: Patrick Reck Источник Размещён: 13.02.2013 12:23

Ответы (6)


1 плюс

237702 Репутация автора

Вы закрываете ResultSetи, следовательно, вы больше не можете его использовать.

Чтобы вернуть содержимое таблицы, вам нужно будет выполнить итерацию ResultSetи построить представление для каждой строки ( Listвозможно, в?). Предположительно, каждая строка представляет какую-то сущность, и я бы создал такую ​​сущность для каждой строки.

while (rs.next()) {
   list.add(new Entity(rs));
}
return list;

Альтернативой является предоставление некоторого объекта обратного вызова, и ваша ResultSetитерация будет вызывать этот объект для каждой ResultSetстроки. Таким образом, вам не нужно создавать объект, представляющий всю таблицу (что может быть проблемой, если она имеет размер)

   while (rs.next()) {
      client.processResultSet(rs);
   }

Я бы не хотел, чтобы клиенты закрывали набор результатов / утверждение / соединение. Этим нужно управлять осторожно, чтобы избежать утечек ресурсов, и вам гораздо лучше справиться с этим в одном месте (желательно рядом с тем местом, где вы их открываете!).

Примечание: вы можете использовать Apache Commons DbUtils.closeQuietly (), чтобы просто и надежно закрыть кортеж connect / Statement / resultset ( правильно обрабатывая нули и исключения)

Автор: Brian Agnew Размещён: 13.02.2013 12:25

7 плюса

249512 Репутация автора

Ну, вы делаете вызов rs.close()в вашем finally-блоке.

В принципе, это хорошая идея, так как вы должны закрыть все свои ресурсы (соединения, операторы, наборы результатов, ...).

Но вы должны закрыть их после их использования.

Есть как минимум три возможных решения:

  1. не закрывайте набор результатов (и соединение, ...) и не требуйте, чтобы вызывающая сторона вызывала отдельный метод «close».

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

  2. пусть вызывающая сторона передает класс, который получает набор результатов, и вызывает его в вашем методе

    Это работает, но может стать немного многословным, так как вам понадобится подкласс некоторого интерфейса (возможно, как анонимный внутренний класс) для каждого блока кода, который вы хотите выполнить в наборе результатов.

    Интерфейс выглядел так:

    public interface ResultSetConsumer<T> {
      public T consume(ResultSet rs);
    }
    

    и ваш selectметод выглядел так:

    public <T> List<T> select(String query, ResultSetConsumer<T> consumer) {
      Connection con = null;
      Statement st = null;
      ResultSet rs = null;
    
        try {
          con = DriverManager.getConnection(url, user, password);
          st = con.createStatement();
    
          rs = st.executeQuery(query);
          List<T> result = new ArrayList<T>();
          while (rs.next()) {
              result.add(consumer.consume(rs));
          }
        } catch (SQLException ex) {
          // logging
        } finally {
          try {
            if (rs != null) {
                rs.close();
            }
            if (st != null) {
                st.close();
            }
            if (con != null) {
                con.close();
            }
          } catch (SQLException ex) {
            Logger lgr = Logger.getLogger(MySQL.class.getName());
            lgr.log(Level.WARNING, ex.getMessage(), ex);
          }
        }
      return rs;
    }
    
  3. выполнить всю работу внутри selectметода и вернуть часть Listв результате.

    Это, вероятно, наиболее широко используемый метод: переберите набор результатов и преобразуйте данные в пользовательские данные в ваших собственных DTO и возвращайте их .

Автор: Joachim Sauer Размещён: 13.02.2013 12:26

51 плюса

896945 Репутация автора

Решение

Вы никогда не должны проходить ResultSetчерез публичные методы. Это может привести к утечке ресурсов, потому что вы вынуждены держать оператор и соединение открытым. Закрытие их неявно закроет набор результатов. Но если они останутся открытыми, это приведет к тому, что они будут болтаться, а в БД не хватит ресурсов, когда их будет слишком много.

Сопоставьте его с коллекцией Javabeans, например, так и верните вместо этого:

public List<Biler> list() throws SQLException {
    Connection connection = null;
    PreparedStatement statement = null;
    ResultSet resultSet = null;
    List<Biler> bilers = new ArrayList<Biler>();

    try {
        connection = database.getConnection();
        statement = connection.prepareStatement("SELECT id, name, value FROM Biler");
        resultSet = statement.executeQuery();

        while (resultSet.next()) {
            Biler biler = new Biler();
            biler.setId(resultSet.getLong("id"));
            biler.setName(resultSet.getString("name"));
            biler.setValue(resultSet.getInt("value"));
            bilers.add(biler);
        }
    } finally {
        if (resultSet != null) try { resultSet.close(); } catch (SQLException ignore) {}
        if (statement != null) try { statement.close(); } catch (SQLException ignore) {}
        if (connection != null) try { connection.close(); } catch (SQLException ignore) {}
    }

    return bilers;
}

Или, если вы уже используете Java 7, просто используйте оператор try-with-resources, который автоматически закроет эти ресурсы:

public List<Biler> list() throws SQLException {
    List<Biler> bilers = new ArrayList<Biler>();

    try (
        Connection connection = database.getConnection();
        PreparedStatement statement = connection.prepareStatement("SELECT id, name, value FROM Biler");
        ResultSet resultSet = statement.executeQuery();
    ) {
        while (resultSet.next()) {
            Biler biler = new Biler();
            biler.setId(resultSet.getLong("id"));
            biler.setName(resultSet.getString("name"));
            biler.setValue(resultSet.getInt("value"));
            bilers.add(biler);
        }
    }

    return bilers;
}

Кстати, вы не должны быть декларантом Connection, Statementи ResultSetпеременные экземпляра на всех (главная проблема threadsafety!), Ни быть проглатывания SQLExceptionв тот момент вообще (абонент не будет иметь ни малейшего представления о том , что проблема произошла), ни быть закрытия ресурсы в том же try(если, например, закрытие результирующего набора выдает исключение, то оператор и соединение все еще открыты). Все эти проблемы исправлены в приведенных выше фрагментах кода.

Автор: BalusC Размещён: 13.02.2013 12:30

14 плюса

141 Репутация автора

Если вы не знаете, что вам нужно от ResultSet при получении времени, я предлагаю сопоставить все это на карте следующим образом:

    List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
    Map<String, Object> row = null;

    ResultSetMetaData metaData = rs.getMetaData();
    Integer columnCount = metaData.getColumnCount();

    while (rs.next()) {
        row = new HashMap<String, Object>();
        for (int i = 1; i <= columnCount; i++) {
            row.put(metaData.getColumnName(i), rs.getObject(i));
        }
        resultList.add(row);
    }

Таким образом, в основном у вас есть то же самое, что и ResultSet (без ResultSetMetaData).

Автор: Manuel Eder Размещён: 13.02.2013 12:59

4 плюса

9048 Репутация автора

Как все до меня говорили, плохая идея передать набор результатов. Если вы используете библиотеку пула соединений, например c3p0, вы можете безопасно использовать CachedRowSet и его реализацию CachedRowSetImpl . Используя это, вы можете закрыть соединение. Он будет использовать соединение только при необходимости. Вот фрагмент из документа Java:

Объект CachedRowSet - это отключенный набор строк, что означает, что он использует соединение с источником данных только на короткое время. Он подключается к своему источнику данных во время чтения данных, чтобы заполнить себя строками, и снова во время передачи изменений обратно в свой базовый источник данных. В остальное время объект CachedRowSet отключается, в том числе во время изменения его данных. Отключение делает объект RowSet намного более компактным и, следовательно, намного легче передать другому компоненту. Например, отключенный объект RowSet может быть сериализован и передан по проводной связи тонкому клиенту, такому как персональный цифровой помощник (PDA).

Вот фрагмент кода для запроса и возврата ResultSet:

public ResultSet getContent(String queryStr) {
    Connection conn = null;
    Statement stmt = null;
    ResultSet resultSet = null;
    CachedRowSetImpl crs = null;
    try {
        Connection conn = dataSource.getConnection();
        stmt = conn.createStatement();
        resultSet = stmt.executeQuery(queryStr);

        crs = new CachedRowSetImpl();
        crs.populate(resultSet);
    } catch (SQLException e) {
        throw new IllegalStateException("Unable to execute query: " + queryStr, e);
    }finally {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException e) {
            LOGGER.error("Ignored", e);
        }
    }

    return crs;
}

Вот фрагмент для создания источника данных с использованием c3p0:

 ComboPooledDataSource cpds = new ComboPooledDataSource();
            try {
                cpds.setDriverClass("<driver class>"); //loads the jdbc driver
            } catch (PropertyVetoException e) {
                e.printStackTrace();
                return;
            }
            cpds.setJdbcUrl("jdbc:<url>");
            cpds.setMinPoolSize(5);
            cpds.setAcquireIncrement(5);
            cpds.setMaxPoolSize(20);

 javax.sql.DataSource dataSource = cpds;
Автор: havexz Размещён: 21.06.2014 06:20

3 плюса

41 Репутация автора

Вы можете использовать объект CachedRowSet только для того, что вы хотите:

public CachedRowSetImpl select(String url, String user, String password) {

    CachedRowSetImpl crs = null;

    try (Connection con = DriverManager.getConnection(url, user, password);
         Statement st = con.createStatement();
         ResultSet rs = st.executeQuery("SELECT * FROM biler");) {

        crs = new CachedRowSetImpl();
        crs.populate(rs);

    } catch (SQLException ex) {
        Logger lgr = Logger.getLogger(MySQL.class.getName());
        lgr.log(Level.SEVERE, ex.getMessage(), ex);


    } catch (SQLException ex) {
        Logger lgr = Logger.getLogger(MySQL.class.getName());
        lgr.log(Level.WARNING, ex.getMessage(), ex);
    }

    return crs;
}

Вы можете прочитать документацию здесь: https://docs.oracle.com/javase/7/docs/api/javax/sql/rowset/CachedRowSet.html

Автор: César Zea Gómez Размещён: 17.03.2018 03:52
Вопросы из категории :
32x32