How to implement created_at and updated_at column using Room Persistence ORM tools in android

Asif Mohammad Mollah picture Asif Mohammad Mollah · Feb 22, 2018 · Viewed 9.7k times · Source

How can I implement created_at and updated_at columns using Room Persistence ORM tools in Android, that can update the timestamp automatically when creating or updating a row in a table?

Answer

dphans picture dphans · May 2, 2018

I've research many sites, but still not found any results can handle middleware or something like callbacks when we Query, Insert, Update, or Delete,... methods from DAOs.

As @selvin said, RoomDatabase.Callback interface only call when database created in first time. So, use that interface is incorrect way. So, may be the way below is a trick for me, hope this will help you:

1. We need to create a BaseModel

This model to sure that all models in database always available columns creation_date and modification_date.

abstract class BaseModel: Serializable {

    @PrimaryKey(autoGenerate = true) 
    @Expose
    var id: Long = 0

    @ColumnInfo(name = "description")
    @SerializedName(value = "description")
    var description: String? = null

    @ColumnInfo(name = "creation_date")
    @SerializedName(value = "creation_date")
    var creationDate: Date = Date(System.currentTimeMillis())

    @ColumnInfo(name = "modification_date")
    @SerializedName(value = "modification_date")
    var modificationDate: Date = Date(System.currentTimeMillis())

}

2. Create an BaseDAO

Inside BaseDAO, I also create a wrapper class named DAOWrapper, this class will store all helpful methods that we will use to process data like "middleware" before interact model's data to DAO.

So, why we don't create methods inside BaseDAO? -> We cannot do that! use that way will conflict android architecture and also we will have error from compiler (all methods declared in DAO object requires annotation Update, Query,...).

interface BaseDAO<T> where T: BaseModel {

    fun getAll(): List<T>

    @Insert(onConflict = OnConflictStrategy.ABORT)
    fun insert(modelData: T)

    @Update(onConflict = OnConflictStrategy.ABORT)
    fun update(modelData: T)

    companion object {

        open class DAOWrapper<P, T>(private val daoInstance: T) where T: BaseDAO<P>, P: BaseModel {

            fun insertWithTimestapData(modelData: P) {
                modelData.modificationDate = Date(System.currentTimeMillis())
                [email protected](modelData)
            }

        }

    }

}

3. To use the DAOWrapper

val appDatabase = // Do something to get RoomDatabase instance...
val exampleDao = appDatabase.exampleDAO()
val exampleDaoWrapper = BaseDAO.Companion.DAOWrapper(exampleDao)

val exampleModel = ExampleModel(name = "Example Name")
exampleDaoWrapper.insertWithTimestapData(exampleModel)

References

Below is example instance of RoomDatabase and example model I used in code above:

/** ExampleModel.kt */
@Entity(
    tableName = "examples",
    indices = [Index(value = arrayOf("name"), unique = true)]
)
class ExampleModel(): BaseModel() {

    @ColumnInfo(name = "name")
    @SerializedName(value = "name")
    var name: String = String()

    @Ignore
    constructor(name: String): this() {
        [email protected] = name
    }

}


/** ExampleDAO.kt */
@Dao
interface ExampleDAO: BaseDAO<ExampleModel> {

    @Query("SELECT * FROM `examples`")
    override fun getAll(): List<ExampleModel>

}


/** AppDatabase.kt **/
@Database(entities = [ExampleModel::class], version = 1)
abstract class AppDatabase: RoomDatabase() {

    abstract fun exampleDAO(): ExampleDAO

    companion object {
        private var databaseInstance: AppDatabase? = null
        public const val DatabaseName: String = "app_database"

        fun getDatabase(context: Context): AppDatabase {
            [email protected](context)
            return [email protected]!!
        }

        fun destroyAndCreateNewInstanceIfNeeded(context: Context) {
            synchronized(AppDatabase::class) {
                [email protected]?.close()
                [email protected] = Room.databaseBuilder(
                    context.applicationContext,
                    AppDatabase::class.java,
                    [email protected]
                ).build()
            }
        }
    }

}