Для некоторых операторов sql я не могу использовать подготовленный статус, например:
SELECT MAX(AGE) FROM ?
Например, когда я хочу изменить таблицу. Есть ли утилита, которая очищает sql в Java? Есть один в рубине.
Для некоторых операторов sql я не могу использовать подготовленный статус, например:
SELECT MAX(AGE) FROM ?
Например, когда я хочу изменить таблицу. Есть ли утилита, которая очищает sql в Java? Есть один в рубине.
Верно, подготовленные параметры запроса запроса можно использовать только там, где вы использовали бы одно буквальное значение. Вы не можете использовать параметр для имени таблицы, имени столбца, списка значений или любого другого синтаксиса SQL.
Таким образом, вы должны интерполировать переменную вашего приложения в строку SQL и соответствующим образом цитировать строку. Используйте цитирование, чтобы разграничить идентификатор имени таблицы, и экранируйте строку кавычек, удвоив ее:
java.sql.DatabaseMetaData md = conn.getMetaData();
String q = md.getIdentifierQuoteString();
String sql = "SELECT MAX(AGE) FROM %s%s%s";
sql = String.format(sql, q, tablename.replaceAll(q, q+q), q);
Например, если имя вашей таблицы буквально table"name
, а символ кавычек вашего идентификатора РСУБД — "
, то sql
должен содержать строку вида:
SELECT MAX(AGE) FROM "table""name"
Я также согласен с комментарием @ChssPly76 - лучше всего, если ваш пользовательский ввод на самом деле не является буквальным именем таблицы, а означает, что ваш код отображается в имя таблицы, которое затем интерполируется в SQL-запрос. Это дает вам больше уверенности в том, что SQL-инъекция не может произойти.
HashMap h = new HashMap<String,String>();
/* user-friendly table name maps to actual, ugly table name */
h.put("accounts", "tbl_accounts123");
userTablename = ... /* user input */
if (h.containsKey(userTablename)) {
tablename = h.get(userTablename);
} else {
throw ... /* Exception that user input is invalid */
}
String sql = "SELECT MAX(AGE) FROM %s";
/* we know the table names are safe because we wrote them */
sql = String.format(sql, tablename);
Невозможно. Лучшее, что вы можете сделать, это использовать String#format()
.
String sql = "SELECT MAX(AGE) FROM %s";
sql = String.format(sql, tablename);
Обратите внимание, что это не позволяет избежать рисков SQL-инъекций. Если tablename
является значением, контролируемым пользователем/клиентом, вам необходимо очистить его с помощью String#replaceAll()
.
tablename = tablename.replaceAll("[^\\w]", "");
Надеюсь это поможет.
[Изменить] Я должен добавить: НЕ используйте это для значений столбцов, для которых вы можете использовать PreparedStatement
. Просто продолжайте использовать его обычным способом для любых значений столбца.
[Edit2] Лучше всего не позволять пользователю/клиенту вводить имя таблицы так, как он хочет, но лучше предоставить раскрывающийся список, содержащий все допустимые имена таблиц (которые вы можете получить с помощью DatabaseMetaData#getCatalogs()
) в пользовательском интерфейсе, чтобы пользователь/клиент может выбрать его. Не забудьте проверить на стороне сервера, верен ли выбор, потому что можно подделать параметры запроса.
В этом случае вы можете проверить имя таблицы по списку доступных таблиц, получив список таблиц из DatabaseMetaData. На самом деле, вероятно, было бы проще использовать регулярное выражение для удаления пробелов, возможно, также некоторых зарезервированных слов sql, «;» и т. д. из строки, прежде чем использовать что-то вроде String.format для создания вашего полного оператора sql.
Причина, по которой вы не можете использовать prepareStatement, заключается в том, что он, вероятно, заключает имя таблицы в ' и экранирует его как строку.