次のようなRoomデータベースDAOがあるとします:
import Android.Arch.persistence.room.Dao;
import Android.Arch.persistence.room.Query;
import Java.util.Date;
import Java.util.List;
@Dao
public interface MyDao {
@Query("SELECT * FROM MyTable")
List<MyItem> all();
@Query("SELECT * FROM MyTable WHERE date = :date AND language = :language")
MyItem byDate(Date date, String language);
}
LoggerまたはMyDao
に追加されたようなものを使用して、どのステートメントが実行されているかを確認する方法はありますか。これは、関数が期待されるSQLステートメントに正しく変換されているかどうかをすぐに確認できるため、開発中に非常に役立ちます。
document Roomのように、コンパイル時のチェックを実行するため、SQLステートメントが有効でない場合、コンパイル自体が失敗し、適切なエラーメッセージがログに表示されます。
また、生成されたコードはデフォルトでデバッグ可能であり、下記のパスにあります。
ビルド>生成>ソース> apt>パッケージ> yourDao_Impl.Java
このクラスには、プロジェクト内の他のクラスをデバッグするときにこのクラスをデバッグできるDAOの実装が含まれています。 :-)
例:
DAOレベルでは、そのためのフックは存在しないようです。データベースのオープンとアップグレードに関連するコールバックがありますが、任意のものではありません。
ただし、 機能要求を提出する が可能です。私はそれが役に立つかもしれないことに同意します。さらに良いのは、OkHttpスタイルの汎用インターセプターフレームワークです。
ルームdb Androidはデバッグコンソールにエラーを表示しません。デバッグ中に何が起こるかを確認する方法を見つけました。
try { someSource.update(someRow) } catch (e: Throwable) { println(e.message) }
出力は次のとおりです。
UNIQUE制約の失敗:quiz.theme(コード2067)
RoomがフレームワークのSqliteを基礎となるデータベースとして使用していると仮定すると、ステートメントは非常に簡単に記録できます。唯一の制限:これはemulatorでのみ実行できます。
SQLiteDebug.Java から:
/** * Controls the printing of SQL statements as they are executed. * * Enable using "adb Shell setprop log.tag.SQLiteStatements VERBOSE". */ public static final boolean DEBUG_SQL_STATEMENTS = Log.isLoggable("SQLiteStatements", Log.VERBOSE);
デフォルトでは、log.tag.SQLiteStatements
の値は設定されていません。
alex @ mbpro:〜$adb Shell getprop log.tag.SQLiteStatements
<-空白行->
上記のドキュメントによると、プロパティを設定するには、使用する必要があります。
alex @ mbpro:〜$adbシェルsetprop log.tag.SQLiteStatements VERBOSE
alex @ mbpro:〜$adb Shell getprop log.tag.SQLiteStatements
詳細
ご覧のとおり、VERBOSE
値が正常に設定されました。ただし、アプリケーションを再実行すると、これらのステートメントは出力されません。動作させるには、 すべてのサービスを再起動adb Shell stop
を使用してからadb Shell start
を使用する必要があります。
通常のデバイスでそれを行おうとすると、次のエラーが表示されます(Pixel XL/stock Android 9)で試しました):
alex @ mbpro:〜$adb Shell start
start:ルートである必要があります
alex @ mbpro:〜$adb root
adbdは実動ビルドでrootとして実行できません
これが、エミュレータを使用する必要がある理由です。
alex @ mbpro:〜$adb root
adbdをルートとして再起動
alex @ mbpro:〜$adb Shell stop
alex @ mbpro:〜$adb Shell start
エミュレータが再起動します。
アプリケーションを実行すると、logcatに同様のSqliteステートメントが表示されます。
<redacted..>
V/SQLiteStatements: <redacted>/my_db: "BEGIN EXCLUSIVE;"
V/SQLiteStatements: <redacted>/my_db: "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)"
V/SQLiteStatements: <redacted>/my_db: "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, "3cb5664b6da264c13388292d98141843")"
V/SQLiteStatements: <redacted>/my_db: "CREATE TABLE IF NOT EXISTS `MyTable` (`id` TEXT NOT NULL, `date` INTEGER, `language` TEXT, PRIMARY KEY(`id`))"
<redacted..>
V/SQLiteStatements: <redacted>/my_db: "BEGIN EXCLUSIVE;"
V/SQLiteStatements: <redacted>/my_db: "PRAGMA temp_store = MEMORY;"
V/SQLiteStatements: <redacted>/my_db: "PRAGMA recursive_triggers='ON';"
V/SQLiteStatements: <redacted>/my_db: "CREATE TEMP TABLE room_table_modification_log(version INTEGER PRIMARY KEY AUTOINCREMENT, table_id INTEGER)"
V/SQLiteStatements: <redacted>/my_db: "COMMIT;"
<redacted..>
V/SQLiteStatements: <redacted>/my_db: "SELECT * FROM MyTable"
V/SQLiteStatements: <redacted>/my_db: "SELECT * FROM MyTable WHERE date = 1551562171387 AND language = 'en'"
変更を取り消すには、次のコマンドを使用します。
alex @ mbpro:〜$adbシェルsetprop log.tag.SQLiteStatements\"\"
alex @ mbpro:〜$adb Shell getprop log.tag.SQLiteStatements
<-空白行->
alex @ mbpro:〜$adb Shell stop
alex @ mbpro:〜$adb Shell start
alex @ mbpro:〜$adb unroot
adbdを非ルートとして再起動
Selectクエリのハックで実現できました。これは挿入/更新/削除操作では機能しません:)
次のように別個のクラスRoomLoggingHelper
を作成します
import Android.annotation.SuppressLint
import androidx.room.RoomSQLiteQuery
private const val NULL = 1
private const val LONG = 2
private const val DOUBLE = 3
private const val STRING = 4
private const val BLOB = 5
private const val NULL_QUERY = "NULL"
const val ROOM_LOGGING_TAG = "roomQueryLog"
object RoomLoggingHelper {
@SuppressLint("RestrictedApi")
fun getStringSql(query: RoomSQLiteQuery): String {
val argList = arrayListOf<String>()
val bindingTypes = query.getBindingTypes()
var i = 0
while (i < bindingTypes.size) {
val bindingType = bindingTypes[i]
when (bindingType) {
NULL -> argList.add(NULL_QUERY)
LONG -> argList.add(query.getLongBindings()[i].toString())
DOUBLE -> argList.add(query.getDoubleBindings()[i].toString())
STRING -> argList.add(query.getStringBindings()[i].toString())
}
i++
}
return String.format(query.sql.replace("?", "%s"), *argList.toArray())
}
fun getStringSql(query: String?, args: Array<out Any>?): String? {
return if (query != null && args != null) {
String.format(query.replace("?", "%s"), *args)
} else
""
}
}
private fun RoomSQLiteQuery.getBindingTypes(): IntArray {
return javaClass.getDeclaredField("mBindingTypes").let { field ->
field.isAccessible = true
return@let field.get(this) as IntArray
}
}
private fun RoomSQLiteQuery.getLongBindings(): LongArray {
return javaClass.getDeclaredField("mLongBindings").let { field ->
field.isAccessible = true
return@let field.get(this) as LongArray
}
}
private fun RoomSQLiteQuery.getStringBindings(): Array<String> {
return javaClass.getDeclaredField("mStringBindings").let { field ->
field.isAccessible = true
return@let field.get(this) as Array<String>
}
}
private fun RoomSQLiteQuery.getDoubleBindings(): DoubleArray {
return javaClass.getDeclaredField("mDoubleBindings").let { field ->
field.isAccessible = true
return@let field.get(this) as DoubleArray
}
}
private fun RoomSQLiteQuery.getIntBindings(): IntArray {
return javaClass.getDeclaredField("mBindingTypes").let { field ->
field.isAccessible = true
return@let field.get(this) as IntArray
}
}
または、このファイルを here からダウンロードできます
このファイルをプロジェクトに追加し、次のようにRoom Databaseクラスから呼び出します。query
メソッドの両方をこのようにオーバーライドします
override fun query(query: SupportSQLiteQuery?): Cursor {
//This will give you the SQL String
val queryString = RoomLoggingHelper.getStringSql(query as RoomSQLiteQuery)
//You can log it in a way you like, I am using Timber
Timber.d("$ROOM_LOGGING_TAG $queryString")
return super.query(query)
}
override fun query(query: String?, args: Array<out Any>?): Cursor {
//This will give you the SQL String
val queryString = RoomLoggingHelper.getStringSql(query, args)
//You can log it in a way you like, I am using Timber
Timber.d("$ROOM_LOGGING_TAG $queryString")
return super.query(query, args)
}
免責事項:
try-catch
ブロックBlobs
には機能しません