ошибка неверного синтаксиса SQL
Имеется запрос, заданный в таком виде:
String GET_BANK_ACCOUNTS_QUERY = "SELECT * FROM bank_account WHERE merchant_id = ?";
Соответственно, выполняется запрос так:
public static List<BankAccount> getMerchantBankAccounts(String id, Connection connection) throws NoBankAccountsFoundException {
try {
PreparedStatement preparedStatement = connection.prepareStatement(GET_BANK_ACCOUNTS_QUERY);
preparedStatement.setString(1,id);
ResultSet set = preparedStatement.executeQuery(GET_BANK_ACCOUNTS_QUERY);
List<BankAccount> list = new ArrayList<>();
while (set.next()) {
StatusCondition status = (set.getString("status").equals("ACTIVE"))?
StatusCondition.ACTIVE: StatusCondition.DELETED;
list.add(new BankAccount(set.getString("id"), set.getString("merchant_id"),
status, set.getString("account_number"),
set.getTimestamp("created_at").toLocalDateTime()));
}
return list;
} catch (SQLException e) {
throw new NoBankAccountsFoundException("Таких аккаунтов не найдено, проверьте id мерчанта");
}
}
При выполнении команды executeQuery выбрасывается исключение
...right syntax to use near ?
Данные в БД есть и id совпадает с столбцом, по которому и стоит условие WHERE. Подскажите, что может быть не так?
Ответы (3 шт):
Как верно указал @Akina в комментарии, для выполнения PreparedStatement, возвращающего ResultSet следует использовать перегруженный метод без параметров PreparedStatement::executeQuery.
В вашем же случае будет вызываться метод из родительского класса, принимающий сырой текст SQL-запроса Statement::executeQuery(String sql), в котором вместо параметра останется знак вопроса. Об этом случае специально указано в документации по ссылке:
ResultSet executeQuery(String sql) throws SQLException
Executes the given SQL statement, which returns a singleResultSetobject.
Note: This method cannot be called on aPreparedStatementorCallableStatement.
Также следует использовать try-with-resources для корректного закрытия самого запроса и связанного с ним экземпляра ResultSet:
A
ResultSetobject is automatically closed when theStatementobject that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
Также следует пересмотреть логику обработки SQLException и выбрасывания NoBankAccountsFoundException, которое должно возникать, если результирующий список пуст.
Исправленный код:
public static List<BankAccount> getMerchantBankAccounts(String id, Connection connection) throws NoBankAccountsFoundException {
try (PreparedStatement preparedStatement = connection.prepareStatement(GET_BANK_ACCOUNTS_QUERY)) {
preparedStatement.setString(1, id);
ResultSet set = preparedStatement.executeQuery();
List<BankAccount> list = new ArrayList<>();
while (set.next()) {
StatusCondition status = (set.getString("status").equals("ACTIVE")) ?
StatusCondition.ACTIVE: StatusCondition.DELETED;
list.add(new BankAccount(
set.getString("id"),
set.getString("merchant_id"),
status,
set.getString("account_number"),
set.getTimestamp("created_at").toLocalDateTime()
));
}
if (list.isEmpty()) {
throw new NoBankAccountsFoundException("Таких аккаунтов не найдено, проверьте id мерчанта")
}
return list;
} catch (SQLException e) {
throw new RuntimeException("Ошибка выполнения запроса: " + GET_BANK_ACCOUNTS_QUERY + "; id=" + id, e);
}
}
Необходимо вызывать метод ResultSet executeQuery() throws SQLException; из класса PreparedStatement, а Вы вызываете метод ResultSet executeQuery(String sql) throws SQLException; из класса Statement, который реализуется классом PreparedStatement
А проблема заключается в том, что вы уже создали PreparedStatement затем передаёте параметры, а потом пытаете выполнить запрос не передав параметры.
...right syntax to use near ?
MySQL не понимает значения ? в SQL-запросе. Это действительно недопустимый синтаксис SQL. Так что каким-то образом он не был заменен в PreparedStatement. И угадай почему?
PreparedStatement preparedStatement = connection.prepareStatement(GET_BANK_ACCOUNTS_QUERY);
preparedStatement.setString(1,id);
ResultSet set = preparedStatement.executeQuery(GET_BANK_ACCOUNTS_QUERY); // Ошибка!!!
Вы заменяете один запрос другим запросом! Вам нужно вызвать метод PreparedStatement::executeQuery() без аргументов вместо Statement::executeQuery(String).
PreparedStatement preparedStatement = connection.prepareStatement(GET_BANK_ACCOUNTS_QUERY);
preparedStatement.setString(1,id);
ResultSet set = preparedStatement.executeQuery(); // Хорошо!!!
Не связанная с проблемой, в вашем коде происходит утечка ресурсов. Через несколько часов они закончатся в БД, и ваше приложение выйдет из строя. Чтобы исправить это, вам нужно следовать идиоме JDBC по закрытию Connection, Statement и ResultSet в блоке finally блока try, где они были получены или использовать утверждение try-with-resources для автоматического закрытия этих ресурсов. Правда при этом надо учитывать следующее, которое можно прочитать здесь: Что делать со statment, когда выпадет исключение при его закрытии в Java?.
Подробную информацию см. в руководстве по основам JDBC.