【问题标题】:Logging SQL queries in android在android中记录SQL查询
【发布时间】:2011-08-23 10:06:26
【问题描述】:

我正在使用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);
    }
}

【问题讨论】:

  • SQLiteCursorFactory.newCursor() 中是否有某种方式可以访问selectionArgs 以将其包含在Log 的输出中?
  • 仅供记录。 SQLiteCursor(db, masterQuery, editTable, query); 自 API lvl 11 起已弃用,请改用 SQLiteCursor(masterQuery, editTable, query);
  • 上面的SQLiteCursorFactory怎么用?
  • 如果您使用 SQLiteOpenHelper,您只需将 SQLiteCursorFactory 传递给助手构造函数中的父级,如下所示:public DbHelper(Context context) { super(context, DATABASE_NAME, new SQLiteCursorFactory(true), DATABASE_VERSION); }

标签: android sqlite logging


【解决方案1】:

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

【讨论】:

  • -1:记录无法访问的SQL查询无济于事。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    编辑:事实上,您甚至可能不必继承Cursor。只需让您工厂的 newCursor() 方法返回标准 SQLiteCursor,但在此之前记录查询。

    【讨论】:

    • 工作得很好,但这会在绑定任何参数之前显示查询。绑定所有提供的参数后,有没有办法查看查询?
    • 这似乎适用于“读取”查询,但不适用于插入和更新。是否有同样适用于插入和更新的解决方案?
    • 一般如何开启查询日志?日志记录在哪里?
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        【讨论】:

        • 自 API 级别 11 起已弃用 source
        【解决方案7】:
        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 stop adb shell start 这将重新启动一切,您将开始看到很多 SQL。
        • 我没有设法启用 SQLite 详细日志记录,即使根据 android.googlesource.com/platform/frameworks/base.git/+/… ,日志记录选项是有效的并且没有任何改变。
        • 日志记录在哪里?日志猫?
        • 日志出现在 logcat 中,请确保您没有启用过滤器,日志与您的应用程序 ID 无关。此外,如果您想再次禁用日志:adb shell setprop log.tag.SQLiteStatements INFO
        【解决方案8】:

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

        【讨论】:

          【解决方案9】:
          adb shell setprop log.tag.SQLiteLog V
          adb shell setprop log.tag.SQLiteStatements V
          adb 外壳停止
          adb shell 启动

          【讨论】:

          • 对于缺少的uid -> adb shell su 0 start adb shell su 0 stop
          • 可以通过将V 替换为D 来反转此设置,因为这些是日志级别VERBOSE 和DEBUG 的快捷方式。
          • 谢谢!我错过了adb shell setprop log.tag.SQLiteLog V 行,它不想工作。现在可以了!
          猜你喜欢
          • 2011-02-27
          • 1970-01-01
          • 2015-11-20
          • 2016-11-30
          • 2013-10-08
          • 2018-02-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多