Вопрос-ответ

Using Prepared Statements to set Table Name

Использование подготовленных инструкций для задания имени таблицы

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

Ошибка и пример кода показаны ниже.

[Microsoft][ODBC Microsoft Access Driver] Parameter 'Pa_RaM000' specified where a table name is required.



private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [?]"; //?=date
public Execute(String reportDate){
try {

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection conn = DriverManager.getConnection(Display.DB_MERC);
PreparedStatement st = conn.prepareStatement(query1);
st.setString(1, reportDate);
ResultSet rs = st.executeQuery();

Есть мысли о том, что может быть причиной этого?

Переведено автоматически
Ответ 1

Имя таблицы нельзя использовать в качестве параметра. Оно должно быть жестко запрограммировано. Таким образом, вы можете сделать что-то вроде:

private String query1 = "SELECT plantID, edrman, plant, vaxnode FROM [" + reportDate + "?]";
Ответ 2

Вы не можете задать имя таблицы в подготовленном операторе

Как говорилось ранее, невозможно задать имя таблицы в подготовленном операторе с помощью preparedStatement.setString(1, tableName). Также невозможно добавить части SQL-запроса в подготовленную инструкцию (например preparedStatement.addSql(" or xyz is null")).

Как сделать это правильно, не рискуя SQL-инъекциями?

Имя таблицы должно быть вставлено в SQL (или JQL) запрос, который вы хотите выполнить со строковыми операциями, такими как "select * from " + tableName или String.format("select * from %s", tableName)

Но как избежать SQL-инъекций?

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

String tableName;
if(condition) {
tableName = "animal";
} else {
tableName = "plant";
}
final String sqlQuery = "delete from " + tableName;
...

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

if(!tableNamesWhitelist.contains(tableName)) {
throw new IllegalArgumentException(tableName + " is not a valid table name");
}
String sqlQuery = "delete from " + tableName;

или с помощью перечисления:

public enum Table {
ANIMAL("animal"),
PLANT("plant");

private sqlTableName;
private TableName(String sqlTableName) {
this.sqlTableName= sqlTableName;
}
public getSqlTableName() {
return sqlTableName;
}
}

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

например

@DeleteMapping("/{table}")
public String deleteByEnum(@PathVariable("table") Table table) {
final String sqlQuery = "delete from " + table.getSqlTableName();
...
}

Конечно, эти примеры также работают с select, update, ... и возможно множество других реализаций для проверки пользовательского ввода.

Ответ 3

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

String sql = "IF ? = 99\n";
sql += "SELECT * FROM first_table\n";
sql += "ELSE\n";
sql += "SELECT * FROM second_table";
PreparedStatement ps = con.prepareStatement(sql);

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

ps.setInt(1, 99);

Или, если нет, вы задаете ему что-то другое.

Ответ 4

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

Исходя из того факта, что у вас есть переменная table name с (как минимум) двумя именами таблиц, возможно, было бы лучше создать метод, который принимает объект, который вы храните, и возвращает подготовленную инструкцию.

PreparedStatement p = createStatement(table);
java sql