在 Android 中记录 SQL 查询

发布于 2024-11-06 21:16:07 字数 953 浏览 1 评论 0原文

我正在使用 query 函数来为我的表构建 SQL 查询。有没有办法查看运行的实际查询?例如将其记录在某处?

到目前为止,我能做的最好的事情就是使用断点查看游标的成员 mQuery。不过,我很想自动输出查询。这个成员当然不是公共的,也没有 getter。


仅供记录,这里是已接受答案的实现。

/**
 * 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);
    }
}

I am using the query functions in order to build the SQL queries for my tables. Is there a way to see the actual query that is run? For instance log it somewhere?

So far the best I could do was to have a look at the cursor's member mQuery using a breakpoint. I'd love to output the queries automatically though. This member is of course not public and does not have a getter.


Just for the record, here is an implementation of the accepted answer.

/**
 * 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);
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

天涯沦落人 2024-11-13 21:16:07
adb shell setprop log.tag.SQLiteStatements VERBOSE

设置此属性后,不要忘记重新启动您的应用程序。

还可以启用执行时间的记录。更多详细信息可在此处获取: http ://androidxref.com/4.2.2_r1/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDebug.java

adb shell setprop log.tag.SQLiteStatements VERBOSE

Don't forget to restart your app after setting this property.

It is also possible to enable logging of execution time. More details are availabe here: http://androidxref.com/4.2.2_r1/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDebug.java

︶ ̄淡然 2024-11-13 21:16:07

您可以将自己的 SQLiteDatabase.CursorFactory 应用到数据库。 (请参阅openDatabase 参数。)这将允许您创建自己的 Cursor 子类,从而将查询保持在易于访问的字段中。

编辑:事实上,您甚至可能不需要子类化Cursor。只需让工厂的 newCursor() 方法返回一个标准的 SQLiteCursor,但在执行此操作之前记录查询。

You can apply your own SQLiteDatabase.CursorFactory to the database. (See the openDatabase parameters.) This will allow you to create your own subclass of Cursor, which keeps the query in an easily accessible field.

edit: In fact, you may not even have to subclass Cursor. Just have your factory's newCursor() method return a standard SQLiteCursor, but log the query before doing so.

假面具 2024-11-13 21:16:07
adb shell setprop log.tag.SQLiteLog V
adb shell setprop log.tag.SQLiteStatements V
adb shell stop
adb shell start
adb shell setprop log.tag.SQLiteLog V
adb shell setprop log.tag.SQLiteStatements V
adb shell stop
adb shell start
み格子的夏天 2024-11-13 21:16:07

使用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);

Using an SQLiteQueryBuilder it's painfully simple. buildQuery() returns a raw sql string, which can then be logged:

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables(ExampleTable.TABLE_NAME);
String sql = qb.buildQuery(projection, selection, null, null, sortOrder, null);
Log.d("Example", sql);
情定在深秋 2024-11-13 21:16:07

到目前为止,我能做的最好的事情就是使用断点查看游标的成员 mQuery。这个成员当然不是公共的,也没有 getter,因此无法输出它。还有更好的建议吗?

So far the best I could do was to have a look at the cursor's member mQuery using a breakpoint. This member is of course not public and does not have a getter, hence, no way to output it. Any better suggestion?

说好的呢 2024-11-13 21:16:07

如果您使用 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)

If you are using SQLiteDatabase with it's standard methods as insert, update and delete custom CursorFactory will not be working.

I implemented my not very great but working solution based on SQLiteDatabase class. It just repeats logic of insert, update and delete methods but without statements and actually doing the logging of SQL statements.

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());
    }
}

Be aware not to use the logger in release versions. It might increase time of queries executing.
You can check if the build is in debug mode with this code line:

0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE)
你是年少的欢喜 2024-11-13 21:16:07

如果是一次性场景,我建议注入一个错误(例如,输入像 LIEK 而不是 LIKE 这样的表达式!)并观察 Eclipse LogCat 是否有任何错误!哈!

If it is for once off scenario, I would suggest injecting an error (e.g. type in expression like LIEK instead of LIKE!) and watch the Eclipse LogCat for any errors! HTH!

北音执念 2024-11-13 21:16:07

如果您使用 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 异常,但您将能够看到查询

If you are using a ContentProvider to access the DB, this is how I got it logging the queries. Not a perfect solution, but it works for development

@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;
  }

It'll throw a DatabaseNotClosed exception, but you'll be able to see the query

作死小能手 2024-11-13 21:16:07

我个人使用 java.util.Log 和 Log.w("MYAPPNAME", "My text...") 函数来记录文本。它显示在 Eclipse 的日志视图中,并且可以对其进行过滤以仅输出“MYAPPNAME”的日志。

Personnally I log text using java.util.Log and the Log.w("MYAPPNAME", "My text...") function. It shows up in the Log view of Eclipse and it can be filtered to output only the logs for "MYAPPNAME".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文