Як я можу використовувати підготовлені заяви в SQlite в Android?


100

Як я можу використовувати підготовлені заяви в SQlite в Android?


9
Подумайте про зміну прийнятої відповіді.
Сурагч

9
домовились - подумайте про зміну відповіді ... ментальність стада підтримала прийняту відповідь, коли є кращого рішення.
goodguys_activate

4
Пабло, будь ласка, змініть прийняту відповідь ... наведений приклад навіть не працює. І наших низових голосів недостатньо, щоб відкріпити це.
SparK

Відповіді:


21

Я постійно використовую підготовлені заяви в Android, це досить просто:

SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();

81
Я не знаю, як ця відповідь може мати стільки голосів. SQLIteStatement # Execute не слід використовувати для запитів Sql, а лише операторами. Перевірте developer.android.com/reference/android/database/sqlite/…
simao

9
Тоді як ви повинні використовувати підготовлений оператор для запитів даних?
Хуан Мендес

12
Зауважте, що SQLiteStatement.bindXXX()має індекс на основі 1, а не 0, як більшість.
Симулятор

6
@jasonhudgins Чому б просто не замінити свій ВИБІР НА ВСТУП? Я щойно прийшов з цієї теми, де ви плутали початківця
клавішника

35
ідеальний приклад ментального стада, який підтверджує та приймає відповідь, яка є абсолютно невірною

165

Для підготовлених операторів SQLite в Android існує SQLiteStatement . Підготовлені заяви допоможуть вам прискорити продуктивність (особливо для тверджень, які потрібно виконати кілька разів), а також допоможуть уникнути нападів на ін'єкції. Дивіться цю статтю для загального обговорення підготовлених заяв.

SQLiteStatementпризначений для використання з операторами SQL, які не повертають декількох значень. (Це означає, що ви не використовуєте їх для більшості запитів.) Нижче наведено кілька прикладів:

Створіть таблицю

String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();

execute()Метод не повертає значення , тому доцільно використовувати з створювати і видаляти , але не призначені для використання з SELECT, INSERT, DELETE і UPDATE , тому що ці повернені значення. (Але дивіться це питання .)

Вставте значення

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();

Зауважте, що executeInsert()метод використовується, а не execute(). Звичайно, ви б не хотіли завжди вводити одні й ті ж речі в кожен ряд. Для цього можна скористатися палітурками .

String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
SQLiteStatement statement = db.compileStatement(sql);

int intValue = 57;
String stringValue = "hello";

statement.bindLong(1, intValue); // 1-based: matches first '?' in sql string
statement.bindString(2, stringValue);  // matches second '?' in sql string

long rowId = statement.executeInsert();

Зазвичай ви використовуєте підготовлені висловлювання, коли хочете швидко повторити щось (наприклад, ВСТУП) багато разів. Підготовлений оператор робить так, що оператор SQL не потрібно щоразу аналізувати та компілювати. Можна ще більше прискорити роботу, використовуючи транзакції . Це дозволяє застосувати всі зміни відразу. Ось приклад:

String stringValue = "hello";
try {

    db.beginTransaction();
    String sql = "INSERT INTO table_name (column_1, column_2) VALUES (?, ?)";
    SQLiteStatement statement = db.compileStatement(sql);

    for (int i = 0; i < 1000; i++) {
        statement.clearBindings();
        statement.bindLong(1, i);
        statement.bindString(2, stringValue + i);
        statement.executeInsert();
    }

    db.setTransactionSuccessful(); // This commits the transaction if there were no exceptions

} catch (Exception e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

Ознайомтеся з цими посиланнями для отримання додаткової корисної інформації про транзакції та прискорення вставок у базу даних.

Оновити рядки

Це основний приклад. Ви також можете застосувати поняття з розділу вище.

String sql = "UPDATE table_name SET column_2=? WHERE column_1=?";
SQLiteStatement statement = db.compileStatement(sql);

int id = 7;
String stringValue = "hi there";

statement.bindString(1, stringValue);
statement.bindLong(2, id);

int numberOfRowsAffected = statement.executeUpdateDelete();

Видалити рядки

executeUpdateDelete()Метод також може бути використаний для ВЕЬЕТЕ і був введений в API 11. Див цей Q & A .

Ось приклад.

try {

    db.beginTransaction();
    String sql = "DELETE FROM " + table_name +
            " WHERE " + column_1 + " = ?";
    SQLiteStatement statement = db.compileStatement(sql);

    for (Long id : words) {
        statement.clearBindings();
        statement.bindLong(1, id);
        statement.executeUpdateDelete();
    }

    db.setTransactionSuccessful();

} catch (SQLException e) {
    Log.w("Exception:", e);
} finally {
    db.endTransaction();
}

Запит

Зазвичай при запуску запиту потрібно повернути курсор з великою кількістю рядків. Але це не для чого SQLiteStatement. Ви не запускаєте з ним запит, якщо вам не потрібен лише простий результат, наприклад, кількість рядків у базі даних, з якими ви можете виконатиsimpleQueryForLong()

String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();

Зазвичай для запуску курсору ви запускаєте query()метод SQLiteDatabase .

SQLiteDatabase db = dbHelper.getReadableDatabase();
String table = "table_name";
String[] columnsToReturn = { "column_1", "column_2" };
String selection = "column_1 =?";
String[] selectionArgs = { someValue }; // matched to "?" in selection
Cursor dbCursor = db.query(table, columnsToReturn, selection, selectionArgs, null, null, null);

Дивіться цю відповідь для отримання більш детальної інформації про запити.


5
Лише нагадування: методи .bindString / .bindLong / ... всі засновані на 1 основі.
Denys Vitali

Я шукав під капотом зручні для Android методи, такі як .query, .insert і .delete, і помітив, що вони використовують SQLiteStatement під кришкою. Чи не було б простіше просто використовувати методи зручності, а не будувати власні заяви?
Nicolás Carrasco

@ NicolásCarrasco, минуло певний час, коли я працював над цим, тому зараз я трохи іржавий. Для запитів та одинарних вставок, оновлень та видалень я б точно використовував зручні методи. Однак якщо ви робите масові вставки, оновлення чи видалення, я б розглядав підготовлені заяви разом із транзакцією. Щодо використання SQLiteStatement, що використовується під кришкою, та чи досить зручні методи зручності, я не можу з цим говорити. Я думаю, я б сказав, якщо зручні методи працюють досить швидко для вас, то використовуйте їх.
Сурагч

Чому ви використовуєте clearBindings ()? Ви пов'язуєте всі свої цінності без будь-яких умов. Немає сенсу мені встановлювати їх першими і нулювати реальне значення.
Неймовірний січень

@TheincredibleJan, я не знаю точно. Це може не бути необхідним, і ви можете спробувати його, не очищаючи прив'язки, щоб побачити, чи це має значення. Однак, сказане, виклик clearBindings()не просто встановлює їх null(див. Вихідний код ). Я розглядаю це як очищення стану, щоб нічого не впливати на це з попереднього циклу. Можливо, це не потрібно. Буду радий тому, хто вміє коментувати.
Сурагч

22

Якщо ви хочете, щоб курсор повернувся, ви можете розглянути щось подібне:

SQLiteDatabase db = dbHelper.getWritableDatabase();

public Cursor fetchByCountryCode(String strCountryCode)
{
    /**
     * SELECT * FROM Country
     *      WHERE code = US
     */
    return cursor = db.query(true, 
        "Country",                        /**< Table name. */
        null,                             /**< All the fields that you want the 
                                                cursor to contain; null means all.*/
        "code=?",                         /**< WHERE statement without the WHERE clause. */
        new String[] { strCountryCode },    /**< Selection arguments. */
        null, null, null, null);
}

/** Fill a cursor with the results. */
Cursor c = fetchByCountryCode("US");

/** Retrieve data from the fields. */
String strCountryCode = c.getString(cursor.getColumnIndex("code"));

/** Assuming that you have a field/column with the name "country_name" */
String strCountryName = c.getString(cursor.getColumnIndex("country_name"));

Дивіться цей фрагмент Genscripts у випадку, якщо ви хочете більш повний. Зауважте, що це параметризований SQL-запит, тому по суті це підготовлений оператор.


Невелика помилка у наведеному вище коді: це повинен бути "новий рядок [] {strCountryCode}", а не "новий рядок {strCountryCode}".
П’єр-Люк Сімар

Перед тим, як отримати дані, потрібно перемістити курсор
Чин

9

Приклад Jasonhudgins не буде працювати. Ви не можете виконати запит stmt.execute()і повернути значення (або а Cursor) назад.

Ви можете лише заздалегідь компілювати висловлювання, які або не повертають жодних рядків (наприклад, вставки або створення оператора таблиці), або одного рядка та стовпця (та використання simpleQueryForLong()або simpleQueryForString()).


1

Щоб отримати курсор, ви не можете використовувати скомпільований стан. Однак, якщо ви хочете використовувати повністю підготовлений оператор SQL, я рекомендую адаптувати метод jbaez ... Використання db.rawQuery()замість db.query().

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.