За някои 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
, а символът за кавички на вашия RDBMS идентификатор е "
, тогава 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 израз.
Причината, поради която не можете да използвате createdStatement, е, че той вероятно затваря името на таблицата в ''s и го екранира като низ.