How to check if data is inserted in Room Database

2020-07-05 05:51发布

问题:

I am using Room Database from the new Architecture components in my project. I am adding some data through dao, but when trying to retrieve it I am not getting it. Can you please suggest me how to check whether the insert was successful or not? Below are the codes to help you understand the problem.

adding to database, I checked with debugger, this statement executed successfully.

appContext.db.rallyDAO().addVehicleListItem(vehicle)

Getting null from database on this statement after insert.

val v = appContext.db.rallyDAO().getVehicleListItem(it.vehicleID)

RoomDatabase

@Database(entities = arrayOf(Rally::class, Route::class, CheckPoints::class, Vehicles::class, VehicleListItem::class), version = 1)
abstract class TSDRoom: RoomDatabase() {
    public abstract fun rallyDAO():RallyDAO
}

Inside DAO

@Insert(onConflict = OnConflictStrategy.REPLACE)
    fun addVehicleListItem(vehicleListItem:VehicleListItem)

    @Query("select * from vehicles_submitted where vehicle_id LIKE :vehicleID")
    fun getVehicleListItem(vehicleID:String):VehicleListItem

VehicleListItem Entity

@Entity(tableName = "vehicles_submitted",
        foreignKeys = arrayOf(ForeignKey(entity = Rally::class,
                parentColumns = arrayOf("rally_id"),
                childColumns = arrayOf("rally_id"))))
class VehicleListItem {
    @PrimaryKey
    @ColumnInfo(name = "vehicle_id")
    @SerializedName("vehicle_id")
    var vehicleID : String = ""
    @ColumnInfo(name = "driver_id")
    @SerializedName("driver_id")
    var driverID : String = ""
    @ColumnInfo(name = "vehicle_name")
    @SerializedName("vehicle_name")
    var vehicleName : String = ""
    @ColumnInfo(name = "driver_name")
    @SerializedName("driver_name")
    var driverName : String = ""
    @ColumnInfo(name = "driver_email")
    @SerializedName("driver_email")
    var driverEmail : String = ""

    @ColumnInfo(name = "rally_id")
    @SerializedName("rally_id")
    var rallyID: String = ""

    @ColumnInfo(name = "is_passed")
    @SerializedName("is_passed")
    var isPassed = false

    @ColumnInfo(name = "passing_time")
    @SerializedName("passing_time")
    var passingTime:String=""
}

回答1:

The problem was in the thread. Room doesn't allow you to run database queries in main thread. The call to insert method was inside of a try-catch block, and I was ignoring the exception. I fixed it, and here's how it reads right now.

doAsync {
                        val addedID = appContext.db.rallyDAO().addVehicleListItem(vehicle)
                        Logger.d("vehicle_lsit_item","Inserted ID $addedID")
        }

Also, I reviewed the documentation, the insert method may return a Long (or List of Long in case of List passed to insert). I also changed the signature of insert, and here is the modified code

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun addVehicleListItem(vehicleListItem:VehicleListItem):Long

P.S. I am using anko for doAsync



回答2:

You can use Stetho to see your DB and Shared pref file in chrome dev tool

http://facebook.github.io/stetho/



回答3:

An option you may consider, if you have not access permission issues, is to navigate directly into your sqlite instance of the device and query the tables directly there.

If you use the emulator intehgrated with Android Studio or a rooted phone you can do the following:

adb root
adb remount
cd data/data/path/of/your/application/database
sqlite3 mydb.db

Then you can query your tables



回答4:

There are multiple ways you can test that.

  1. As @Ege Kuzubasioglu mentioned you can use stetho to check manually (Need minor change to code).
  2. Pull database file from "data/data/yourpackage/databases/yourdatabase.db" to your local machine and use any applications to read the content inside the database. I personally use https://sqlitebrowser.org/. Pulling database file can be done either using the shell commands or use "Device File Explorer" from android studio.

  3. Write TestCase to see if it is working. Here is an example of test case from one of my projects.

    // Code from my DAO class

    @Insert(onConflict = OnConflictStrategy.REPLACE) public abstract Long[] insertPurchaseHistory(List MusicOrders);

//My Test Case @Test public void insertPurchaseHistoryTest() {

    // Read test data from "api-responses/music-purchase-history-response.json"
    InputStream inputStream = getClass().getClassLoader()
            .getResourceAsStream("api-responses/music-purchase-history-response.json");
    // Write utility method to convert your stream to a string.
    String testData = FileManager.readFileFromStream(inputStream);

    // Convert test data to "musicOrdersResponse"
    MusicOrdersResponse musicOrdersResponse = new Gson().fromJson(testData,MusicOrdersResponse.class);

    // Insert inmateMusicOrders and get the list of
    Long[] rowsInserted = tracksDao.insertPurchaseHistory(musicOrdersResponse.getmusicOrders());
    assertThat(rowsInserted.length,Matchers.greaterThan(0));
}


回答5:

You can use Android Debug Database to access your application's databases via a web browser. Android Debug Database exposes the databases via an embedded web server.

Use it as follows:

  1. Include it as debugImplementation dependency in your app's build.gradle so that it will only be included in debug build and not in release build:

    debugImplementation 'com.amitshekhar.android:debug-db:1.0.3'
    
  2. Start the debug build of your app

  3. The embedded web server launches automatically and announces its address and port in the logs:

    D/DebugDB: Open http://XXX.XXX.X.XXX:8080 in your browser
    
  4. If you are running the app over USB, setup portforwarding:

    adb forward tcp:8080 tcp:8080
    

    If you are not running the app over USB, your Android phone and workstation need to be in the same network and your workstation should be able to ping the Android phone.

  5. Finally, open the link from the logs in the browser.

Note that it works just fine without rooting the device as the web server runs inside your app context.



回答6:

I make like this(I use coroutines suspend):

class SaveVehicle(val repository: RepositoryInterface) {
    suspend fun invoke(vehicleListItem: VehicleListItem): VehicleListItem = 
        with(vehicleListItem) {
           also {
                 repository.saveVehicle(vehicleListItem)
           }
        }
}

Kotlin Doc

inline fun <T> T.also(block: (T) -> Unit): T

Calls the specified function block with this value as its argument and returns this value.

also is good for performing some actions that take the context object as an argument. Use also for actions that need a reference rather to the object than to its properties and functions, or when you don't want to shadow this reference from an outer scope.

You can check my example in GitHub: