¿Cómo utilizo declaraciones preparadas en SQlite en Android?
¿Cómo utilizo declaraciones preparadas en SQlite en Android?
Respuestas:
Utilizo declaraciones preparadas en Android todo el tiempo, es bastante simple:
SQLiteDatabase db = dbHelper.getWritableDatabase();
SQLiteStatement stmt = db.compileStatement("INSERT INTO Country (code) VALUES (?)");
stmt.bindString(1, "US");
stmt.executeInsert();
SQLiteStatement.bindXXX()
tiene un índice basado en 1, no basado en 0 como la mayoría.
Para las declaraciones SQLite preparadas en Android, existe SQLiteStatement . Las declaraciones preparadas lo ayudan a acelerar el rendimiento (especialmente para declaraciones que deben ejecutarse varias veces) y también ayudan a evitar ataques de inyección. Consulte este artículo para obtener una discusión general sobre declaraciones preparadas.
SQLiteStatement
está destinado a utilizarse con sentencias SQL que no devuelven varios valores. (Eso significa que no los usaría para la mayoría de las consultas). A continuación se muestran algunos ejemplos:
String sql = "CREATE TABLE table_name (column_1 INTEGER PRIMARY KEY, column_2 TEXT)";
SQLiteStatement stmt = db.compileStatement(sql);
stmt.execute();
El execute()
método no devuelve un valor, por lo que es apropiado usarlo con CREATE y DROP, pero no está destinado a usarse con SELECT, INSERT, DELETE y UPDATE porque estos devuelven valores. (Pero vea esta pregunta ).
String sql = "INSERT INTO table_name (column_1, column_2) VALUES (57, 'hello')";
SQLiteStatement statement = db.compileStatement(sql);
long rowId = statement.executeInsert();
Tenga en cuenta que el executeInsert()
método se utiliza en lugar de execute()
. Por supuesto, no querrá ingresar siempre las mismas cosas en cada fila. Para eso puedes usar enlaces .
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();
Por lo general, usa declaraciones preparadas cuando desea repetir algo rápidamente (como un INSERT) muchas veces. La declaración preparada hace que la declaración SQL no tenga que ser analizada y compilada cada vez. Puede acelerar las cosas aún más mediante transacciones . Esto permite que todos los cambios se apliquen a la vez. Aquí hay un ejemplo:
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();
}
Consulte estos enlaces para obtener más información sobre transacciones y acelerar las inserciones de bases de datos.
Este es un ejemplo básico. También puede aplicar los conceptos de la sección anterior.
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();
El executeUpdateDelete()
método también se puede utilizar para declaraciones DELETE y se introdujo en la API 11. Consulte estas preguntas y respuestas .
Aquí hay un ejemplo.
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();
}
Normalmente, cuando ejecuta una consulta, desea recuperar un cursor con muchas filas. Sin SQLiteStatement
embargo, no es para eso . No ejecuta una consulta con él a menos que solo necesite un resultado simple, como el número de filas en la base de datos, lo que puede hacer consimpleQueryForLong()
String sql = "SELECT COUNT(*) FROM table_name";
SQLiteStatement statement = db.compileStatement(sql);
long result = statement.simpleQueryForLong();
Por lo general, ejecutará el query()
método de SQLiteDatabase para obtener un cursor.
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);
Consulte esta respuesta para obtener mejores detalles sobre las consultas.
clearBindings()
no solo los configura en null
(ver el código fuente ). Lo veo como una limpieza del estado para que nada lo influya desde el ciclo anterior. Sin embargo, quizás eso no sea necesario. Me alegraría que alguien que sepa comentar.
Si desea un cursor al regresar, entonces podría considerar algo como esto:
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"));
Vea este fragmento de Genscripts en caso de que desee uno más completo. Tenga en cuenta que esta es una consulta SQL parametrizada, por lo que, en esencia, es una declaración preparada.
El ejemplo de jasonhudgins no funcionará. No puede ejecutar una consulta con stmt.execute()
y recuperar un valor (o a Cursor
).
Solo puede precompilar declaraciones que no devuelvan filas en absoluto (como una declaración de inserción o creación de tabla) o una sola fila y columna (y use simpleQueryForLong()
o simpleQueryForString()
).