Регистрация SQL-запросов в Android

47

Я использую функции 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);
    }
}
  • 2
    Есть ли какой-нибудь способ в SQLiteCursorFactory.newCursor() для доступа к selectionArgs чтобы включить его в вывод в Log ?
  • 2
    Просто для записи. SQLiteCursor(db, masterQuery, editTable, query); устарел, поскольку API lvl 11 использует SQLiteCursor(masterQuery, editTable, query); вместо.
Показать ещё 4 комментария
Теги:
logging

9 ответов

33
Лучший ответ

Вы можете применить свой собственный SQLiteDatabase.CursorFactory к базе данных. (См. Параметры openDatabase.) Это позволит вам создать свой собственный подкласс Cursor, который сохраняет запрос в легкодоступном поле.

edit. На самом деле вам может даже не потребоваться подкласс Cursor. Просто верните метод factory newCursor(), но запустите запрос перед этим.

  • 0
    Спасибо, реализовано и работает хорошо. Награда присуждается.
  • 6
    Работает хорошо, но отображает запрос до того, как будут связаны какие-либо аргументы. Есть ли способ увидеть запрос после того, как все предоставленные аргументы будут связаны?
Показать ещё 2 комментария
31
adb shell setprop log.tag.SQLiteStatements VERBOSE

Не забудьте перезапустить приложение после установки этого свойства.

Также возможно включить ведение журнала времени выполнения. Более подробная информация доступна здесь: http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDebug.java

  • 6
    Перезапуска вашего приложения может быть недостаточно. Если нет, запустите их тоже: adb shell stop adb shell start Это перезапустит все, и вы начнете видеть много SQL.
  • 0
    включите команду, чтобы отменить это!
Показать ещё 3 комментария
12
adb shell setprop log.tag.SQLiteLog V
adb shell setprop log.tag.SQLiteStatements V
adb shell stop
adb shell start
  • 0
    Для пропущенного uid -> adb shell su 0 start adb shell su 0 stop
  • 0
    эту настройку можно изменить, заменив V на D так как это ярлыки для уровней регистрации VERBOSE и DEBUG.
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);
  • 5
    устаревшим , начиная с API Leve 11 источника
3

Пока лучшее, что я мог сделать, это посмотреть на элемент mQuery-курсора, используя точку останова. Этот член, конечно, не является публичным и не имеет геттера, следовательно, не может его выводить. Любое лучшее предложение?

1

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

Я реализовал свое не очень большое, но работающее решение на основе класса SQLiteDatabase. Он просто повторяет логику методов вставки, обновления и удаления, но без операторов и фактически выполняет ведение журнала 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 && 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 & ApplicationInfo.FLAG_DEBUGGABLE)
1

Если это сценарий отключен, я бы предложил ввести ошибку (например, введите выражение, подобное LIEK, а не LIKE!) и наблюдайте за Eclipse LogCat за любые ошибки! НТН!

0

Если вы используете 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, но вы сможете увидеть запрос

-4

Лично я записываю текст с помощью java.util.Log и Log.w("MYAPPNAME", "My text..."). Он отображается в представлении журнала Eclipse и может быть отфильтрован для вывода только журналов для "MYAPPNAME".

  • 4
    -1: не помогает регистрировать SQL-запрос, к которому нет доступа.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню