База данных журналов DAO для Android

С учетом базы данных 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 или нет.

Ответ 1

На уровне DAO, похоже, нет никаких зацепок для этого. Есть обратные вызовы, связанные с открытием и обновлением базы данных, но не произвольные вещи.

Вы можете подать запрос на функцию, хотя. Я согласен, что это может быть полезно. Еще лучше будет универсальная структура перехватчика в стиле OkHttp.

Ответ 2

Согласно документу Room, он выполняет проверку времени компиляции, поэтому, если ваш SQL-оператор недопустим, сама компиляция не удалась, и в журнале отображается правильное сообщение об ошибке.

Также сгенерированный код является отлаживаемым по умолчанию и может быть найден по указанному ниже пути.

сборка> созданная> исходная> apt> ваш пакет> yourDao_Impl.java

Этот класс содержит реализацию вашего DAO, вы можете отлаживать этот класс по мере отладки других классов в вашем проекте. :-)

Пример:

enter image description here

Ответ 3

Когда у меня есть некоторая неизвестная ошибка при вставке или обновлении строки в комнате db Android не обнаруживает ошибок в консоли отладки. Одна вещь, которую я нашел, как проверить, что происходит во время отладки:

try { someSource.update(someRow) } catch (e: Throwable) { println(e.message) }

Выход:

Ошибка UNIQUE: quiz.theme(код 2067)

Ответ 4

Я смог добиться этого с помощью хака для запросов 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
        [email protected] field.get(this) as IntArray
    }
}

private fun RoomSQLiteQuery.getLongBindings(): LongArray {

    return javaClass.getDeclaredField("mLongBindings").let { field ->
        field.isAccessible = true
        [email protected] field.get(this) as LongArray
    }
}

private fun RoomSQLiteQuery.getStringBindings(): Array<String> {

    return javaClass.getDeclaredField("mStringBindings").let { field ->
        field.isAccessible = true
        [email protected] field.get(this) as Array<String>
    }
}

private fun RoomSQLiteQuery.getDoubleBindings(): DoubleArray {

    return javaClass.getDeclaredField("mDoubleBindings").let { field ->
        field.isAccessible = true
        [email protected] field.get(this) as DoubleArray
    }
}

private fun RoomSQLiteQuery.getIntBindings(): IntArray {

    return javaClass.getDeclaredField("mBindingTypes").let { field ->
        field.isAccessible = true
        [email protected] field.get(this) as IntArray
    }
}

Или вы можете скачать этот файл здесь

Добавьте этот файл в ваш проект и вызовите его из класса 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)
    }

Отказ от ответственности:

  • Я использую Reflection для получения строкового SQL, следовательно, используйте это ТОЛЬКО в режиме отладки
  • Это написано на скорую руку и может содержать ошибки, было бы целесообразно сохранить его в блоке try-catch
  • Кроме того, я проверил его на строковые аргументы, должен работать долго и дважды, не будет работать для Blobs