[Android Kotlin 기초] 7-1. Create a Room Database

Create a Room Database

Database

Room

room

Codelab - Example of Entity using data class

@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
    @PrimaryKey(autoGenerate = true)
    var nightId: Long = 0L,
 
    @ColumnInfo(name = "start_time_milli")
    val startTimeMilli: Long = System.currentTimeMillis(),

    @ColumnInfo(name = "end_time_milli")
    var endTimeMilli: Long = startTimeMilli,

    @ColumnInfo(name = "quality_rating")
    var sleepQuality: Int = -1
)

Codelab - Example of creating DAO

@Dao
interface SleepDatabaseDao {
    @Insert
    fun insert(night: SleepNight)
  
    @Update
    fun update(night: SleepNight)
  
    @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
    fun get(key: Long): SleepNight?
  
    // We can use @Delete as well as @Query, but it is only applicable when you want to delete specific entries.
    @Query("DELETE FROM daily_sleep_quality_table")
    fun clear()
	
    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
    fun getTonight(): SleepNight?
  
    @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
    fun getAllNights(): LiveData<List<SleepNight>>
}

Codelab - Create and test a Room database

// When exportSchema is set to false, this won't keep schema version history backups.
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {
    abstract val sleepDatabaseDao: SleepDatabaseDao
    
    companion object {
        // never be cached, and all R/W will be done from and to the main memory.
        // You can make sure the value of INSTANCE is always up-to-date and the same to all execution threads.
        @Volatile
      	private var INSTANCE: SleepDatabase? = null // singleton

      	fun getInstance(context: Context): SleepDatabase {
            // Thread synchronization, means only one thread of execution at a time can enter this block of code. 
            synchronized(this) {
	        var instance = INSTANCE
            	if (instance == null) {
    		    instance = Room.databaseBuilder(
				  context.applicationContext,
				  SleepDatabase::class.java,
				  "sleep_history_database"
				)
                    .fallbackToDestructiveMigration()
                    .build()
              	    INSTANCE = instance
            	}
            	return instance
            }
      	}
    }
}

Annotate INSTANCE with @Volatile. The value of a volatile variable will never be cached, and all writes and reads will be done to and from the main memory. This helps make sure the value of INSTANCE is always up-to-date and the same to all execution threads. It means that changes made by one thread to INSTANCE are visible to all other threads immediately, and you don’t get a situation where, say, two threads each update the same entity in a cache, which would create a problem.

@RunWith(AndroidJUnit4::class)
class SleepDatabaseTest {
    private lateinit var sleepDao: SleepDatabaseDao
    private lateinit var db: SleepDatabase

    @Before
    fun createDb() {
        val context = InstrumentationRegistry.getInstrumentation().targetContext
        // Using an in-memory database because the information stored here disappears when the
        // process is killed.
        db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java)
                // Allowing main thread queries, just for testing.
                .allowMainThreadQueries()
                .build()
        sleepDao = db.sleepDatabaseDao
    }

    @After
    @Throws(IOException::class)
    fun closeDb() {
        db.close()
    }

    @Test
    @Throws(Exception::class)
    fun insertAndGetNight() {
        val night = SleepNight()
        sleepDao.insert(night)
        val tonight = sleepDao.getTonight()
        assertEquals(tonight?.sleepQuality, -1)
    }
}