#android #sqlite #logging
#Android #sqlite #протоколирование
Вопрос:
Я использую query
функции для построения SQL-запросов для своих таблиц. Есть ли способ увидеть фактический выполняемый запрос? Например, протоколировать это где-нибудь?
Пока лучшее, что я мог сделать, это взглянуть на mQuery-элемент курсора, используя точку останова. Я бы хотел выводить запросы автоматически, хотя. Этот участник, конечно, не является общедоступным и не имеет средства получения.
Просто для записи, вот реализация принятого ответа.
/**
* Implement the cursor factory in order to log the queries before returning
* the cursor
*
* @author Vincent @ MarvinLabs
*/
public class SQLiteCursorFactory implements CursorFactory {
private boolean debugQueries = false;
public SQLiteCursorFactory() {
this.debugQueries = false;
}
public SQLiteCursorFactory(boolean debugQueries) {
this.debugQueries = debugQueries;
}
@Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
String editTable, SQLiteQuery query) {
if (debugQueries) {
Log.d("SQL", query.toString());
}
return new SQLiteCursor(db, masterQuery, editTable, query);
}
}
Комментарии:
1. Есть ли какой-нибудь способ
SQLiteCursorFactory.newCursor()
получить доступ кselectionArgs
, чтобы включить его в выходные данныеLog
?2. Просто для записи.
SQLiteCursor(db, masterQuery, editTable, query);
не рекомендуется с API 11 уровня, используйтеSQLiteCursor(masterQuery, editTable, query);
вместо этого.3. Как вы используете вышеупомянутый SQLiteCursorFactory?
4. Смотрите developer.android.com/reference/android/database/sqlite/…
5. Если вы используете SQLiteOpenHelper, вам просто нужно передать свой SQLiteCursorFactory родительскому элементу в конструкторе вашего помощника, вот так:
public DbHelper(Context context) { super(context, DATABASE_NAME, new SQLiteCursorFactory(true), DATABASE_VERSION); }
Ответ №1:
adb shell setprop log.tag.SQLiteStatements VERBOSE
Не забудьте перезапустить свое приложение после установки этого свойства.
Также можно включить протоколирование времени выполнения. Более подробная информация доступна здесь: http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDebug.java
Комментарии:
1.Перезапуска вашего приложения может быть недостаточно. Если нет, запустите и это тоже:
adb shell stop
adb shell start
Это перезапустит все, и вы начнете видеть много SQL.2. Мне не удалось включить подробное протоколирование SQLite, хотя согласно android.googlesource.com/platform/frameworks/base.git / /… , параметр протоколирования действителен, и ничего не изменилось.
3. Где происходит протоколирование? logcat?
4. Журналы отображаются в logcat, убедитесь, что у вас не включен фильтр, журналы не связаны с вашим идентификатором приложения. Кроме того, если вы хотите снова отключить журналы:
adb shell setprop log.tag.SQLiteStatements INFO
Ответ №2:
Вы можете применить свои собственные SQLiteDatabase.CursorFactory
к базе данных. (Смотрите параметры openDatabase.) Это позволит вам создать свой собственный подкласс Cursor
, который хранит запрос в легкодоступном поле.
редактировать: На самом деле, возможно, вам даже не придется создавать подкласс Cursor
. Просто попросите newCursor()
метод вашего factory вернуть стандартное значение SQLiteCursor
, но перед этим запишите запрос.
Комментарии:
1. Работает хорошо, но при этом запрос отображается до привязки каких-либо аргументов. Есть ли способ просмотреть запрос после привязки всех предоставленных аргументов?
2. Похоже, это работает для запросов «на чтение», но не работает для вставок и обновлений. Есть ли решение, которое также работает для вставок и обновлений?
3. как включить протоколирование запросов вообще? где происходит протоколирование?
Ответ №3:
adb shell setprop log.tag.SQLiteLog V adb shell setprop log.tag.SQLiteStatements V остановка adb shell запуск оболочки adb
Комментарии:
1.Из-за отсутствия uid ->
adb shell su 0 start
adb shell su 0 stop
2. этот параметр можно отменить, заменив
V
наD
, поскольку это ярлыки для уровней ведения журнала VERBOSE и DEBUG.3. Спасибо! Мне не хватало строки
adb shell setprop log.tag.SQLiteLog V
, и это не хотело работать. Теперь это происходит!
Ответ №4:
Использовать SQLiteQueryBuilder
это до боли просто. buildQuery()
возвращает необработанную строку sql, которую затем можно протоколировать:
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(ExampleTable.TABLE_NAME);
String sql = qb.buildQuery(projection, selection, null, null, sortOrder, null);
Log.d("Example", sql);
Комментарии:
1. не рекомендуется с исходного кода API leve 11
Ответ №5:
Пока лучшее, что я мог сделать, это взглянуть на mQuery-элемент курсора, используя точку останова. Этот элемент, конечно, не является общедоступным и не имеет средства получения, следовательно, его невозможно вывести. Есть предложения получше?
Ответ №6:
Если вы используете SQLiteDatabase с его стандартными методами, такими как вставка, обновление и удаление, пользовательский CursorFactory не будет работать.
Я реализовал свое не очень отличное, но рабочее решение на основе класса SQLiteDatabase. Он просто повторяет логику методов insert, update и delete, но без инструкций и фактически выполняет протоколирование инструкций SQL.
public class SQLiteStatementsLogger {
private static final String TAG = SQLiteStatementsLogger.class.getSimpleName();
private static final String[] CONFLICT_VALUES = new String[]
{"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
public void logInsert(String table, String nullColumnHack, ContentValues values) {
logInsertWithOnConflict(table, nullColumnHack, values, 0);
}
public static void logInsertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
StringBuilder sql = new StringBuilder();
sql.append("INSERT");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
sql.append('(');
Object[] bindArgs = null;
int size = (initialValues != null amp;amp; initialValues.size() > 0)
? initialValues.size() : 0;
if (size > 0) {
bindArgs = new Object[size];
int i = 0;
for (String colName : initialValues.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i ] = initialValues.get(colName);
}
sql.append(')');
sql.append(" VALUES (");
for (i = 0; i < size; i ) {
sql.append((i > 0) ? ",?" : "?");
}
} else {
sql.append(nullColumnHack ") VALUES (NULL");
}
sql.append(')');
sql.append(". (");
for (Object arg : bindArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
Log.d(TAG, sql.toString());
}
public static void logUpdate(String table, ContentValues values, String whereClause, String[] whereArgs) {
logUpdateWithOnConflict(table, values, whereClause, whereArgs, 0);
}
public static void logUpdateWithOnConflict(String table, ContentValues values,
String whereClause, String[] whereArgs, int conflictAlgorithm) {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(table);
sql.append(" SET ");
// move all bind args to one array
int setValuesSize = values.size();
int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize whereArgs.length);
Object[] bindArgs = new Object[bindArgsSize];
int i = 0;
for (String colName : values.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i ] = values.get(colName);
sql.append("=?");
}
if (whereArgs != null) {
for (i = setValuesSize; i < bindArgsSize; i ) {
bindArgs[i] = whereArgs[i - setValuesSize];
}
}
if (!TextUtils.isEmpty(whereClause)) {
sql.append(" WHERE ");
sql.append(whereClause);
}
sql.append(". (");
for (Object arg : bindArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
Log.d(TAG, sql.toString());
}
public static void logDelete(String table, String whereClause, String[] whereArgs) {
StringBuilder sql = new StringBuilder("DELETE FROM " table);
if (!TextUtils.isEmpty(whereClause)) {
sql.append(" WHERE " whereClause);
sql.append(". (");
for (Object arg : whereArgs) {
sql.append(String.valueOf(arg)).append(",");
}
sql.deleteCharAt(sql.length()-1).append(')');
}
Log.d(TAG, sql.toString());
}
}
Помните, что не следует использовать регистратор в версиях выпуска. Это может увеличить время выполнения запросов.
Вы можете проверить, находится ли сборка в режиме отладки с помощью этой строки кода:
0 != (getApplicationInfo().flags amp; ApplicationInfo.FLAG_DEBUGGABLE)
Ответ №7:
Если это одноразовый сценарий, я бы предложил ввести ошибку (например, введите выражение like LIEK вместо LIKE!) и просмотрите Eclipse LogCat на предмет любых ошибок! HTH!
Ответ №8:
Если вы используете ContentProvider для доступа к БД, вот как я заставил его протоколировать запросы. Не идеальное решение, но оно работает для разработки
@Override
public boolean onCreate() {
dbHelper = new MySQLiteHelper(getContext());
database=dbHelper.getWritableDatabase();
if(!database.isReadOnly())
database.execSQL("PRAGMA foreign_keys=ON;");
return true;
}
SQLiteDatabase.CursorFactory cursorFactory = new SQLiteDatabase.CursorFactory() {
@Override
public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery, String editTable, SQLiteQuery query) {
Log.d(TAG, "Query: " query);
return new SQLiteCursor(db, masterQuery, editTable, query);
}
};
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
String table =getTableName(uri);
if(Constants.LOG_QUERIES){
database = SQLiteDatabase.openOrCreateDatabase(database.getPath(), cursorFactory);
}
Cursor cursor =database.query(table, projection, selection, selectionArgs, null, null, sortOrder);
cursor.moveToFirst();
return cursor;
}
Это вызовет исключение DatabaseNotClosed, но вы сможете увидеть запрос
Ответ №9:
Лично я регистрирую текст с помощью java.util.Log
и Log.w("MYAPPNAME", "My text...")
функции. Он отображается в представлении журнала Eclipse и может быть отфильтрован для вывода только журналов для «MYAPPNAME».
Комментарии:
1. -1: Это не помогает регистрировать SQL-запрос, к которому невозможно получить доступ.