给出像这样的房间数据库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语句。
答案 0 :(得分:12)
在DAO级别似乎没有任何钩子。有与数据库打开和升级相关的回调,但不是任意的东西。
但你可以file a feature request。我同意它可能有用。更好的是OkHttp风格的通用拦截器框架。
答案 1 :(得分:12)
根据Room的document,它执行编译时间检查,因此如果您的SQL语句无效,编译本身就会失败并且日志中会显示正确的错误消息。
默认情况下,生成的代码也是可调试的,可以在下面提到的路径中找到。
构建&gt;生成&gt;来源&gt; apt&gt;你的包&gt; yourDao_Impl.java
此类包含DAO的实现,您可以在调试项目中的其他类时调试此类。 : - )
示例:
答案 2 :(得分:8)
截至 Room 2.3.0-alpha04(2020 年 12 月 16 日发布,在您阅读本文时可能已经稳定),Room 直接支持使用新的 RoomDatabase.QueryCallback
您在 RoomDatabase.Builder
fun getDatabase(context: Context): MyDatabase {
val dbBuilder = Room.databaseBuilder(
context.applicationContext,
MyDatabase::class.java, "mydatabase.db"
)
dbBuilder.setQueryCallback(RoomDatabase.QueryCallback { sqlQuery, bindArgs ->
println("SQL Query: $sqlQuery SQL Args: $bindArgs")
}, Executors.newSingleThreadExecutor())
return dbBuilder.build()
}
请注意,这只是示例代码,您可能应该确保 MyDatabase
在您的应用中是单例。另一个提示是仅在应用程序处于调试状态时记录查询:
if (BuildConfig.DEBUG) dbBuilder.setQueryCallback(
... 以及上面的其余代码。
如果有人想要 Java 中的示例代码,请评论
答案 3 :(得分:5)
当我在插入或更新房间内的行时遇到一些未知错误时,Android在调试控制台中没有显示任何错误。有一件事我发现如何在调试时检查是什么:
try { someSource.update(someRow) } catch (e: Throwable) { println(e.message) }
输出是:
UNIQUE约束失败:quiz.theme(代码2067)
答案 4 :(得分:2)
我已经能够通过针对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数据库类中调用它,如下所示:
覆盖这两个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
答案 5 :(得分:2)
假定Room使用框架的Sqlite作为基础数据库,则可以很简单地记录语句。唯一的限制:只能在模拟器上完成此操作。
/** * 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 shell setprop log.tag.SQLiteStatements VERBOSE
alex @ mbpro:〜$ adb shell getprop log.tag.SQLiteStatements
详细
我们可以看到,VERBOSE
值已成功设置。但是,如果我们重新运行我们的应用程序-我们将看不到这些语句。要使其正常工作,我们必须先使用adb shell stop
restart all the services,然后使用adb shell start
。
如果您尝试使用常规设备执行此操作,则会收到以下错误(已在Pixel XL / Android 9上尝试过):
alex @ mbpro:〜$ adb shell启动
开始:必须是root
alex @ mbpro:〜$ adb根
adbd无法在生产版本中以root用户身份运行
这就是为什么我们必须使用模拟器:
alex @ mbpro:〜$ adb根
以root身份重新启动adbd
alex @ mbpro:〜$ adb shell停止
alex @ mbpro:〜$ adb shell启动
模拟器将重新启动。
运行您的应用程序,您将在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 shell setprop log.tag.SQLiteStatements \“ \”
alex @ mbpro:〜$ adb shell getprop log.tag.SQLiteStatements
<-空白行->
alex @ mbpro:〜$ adb shell停止
alex @ mbpro:〜$ adb shell启动
alex @ mbpro:〜$ adb取消root
以非root身份重新启动adbd