Search code examples
kotlinandroid-room

Room field initialized at null


I'm editing an existing entity of my project which looks like this (i added the sessionSettings field):

data class Patient(
    
    // ... other fields
    
    @Expose
    @SerializedName("exo_settings")
    @Embedded
    var exoSettingsData: ExoSettingsData? = null,   // already exist

    @Expose
    @SerializedName("session_settings")
    @TypeConverters(SessionSettingsConverters::class)
    var sessionSettings: SessionSettings? = null,   // the new field
    
    /// ... other fields

)

My TypeConverter class:

class SessionSettingsConverters {
    private var gson = Gson()

    @TypeConverter
    fun stringToSessionSettings(data: String?): SessionSettings? {
        if (data.isNullOrEmpty()) {
            return null
        }

        val sessionSettingsType = object : TypeToken<SessionSettings>() {}.type

        return gson.fromJson(data, sessionSettingsType)
    }

    @TypeConverter
    fun sessionSettingsToString(someObject: SessionSettings): String? {
        return gson.toJson(someObject)
    }
}

I added the sessionSettings; which is a basic object containing strings and integers, and it's TypeConverter to ease the integration. My issue is, i need this field to be null by default, because if it is, i'm grabbing data from another source. The problem is if I do so, my unit tests get broken if I do for exemple this:

val patient1 = Patient("1", synkStatus = SynkStatus.DELETED)
centerDatabase.patientDao().savePatient(patient1).test()

It fails (the entity is not saved), but if I initialize a SessionSettings object it works:

val patient1 = Patient("1", synkStatus = SynkStatus.DELETED, sessionSettings = SessionSettings())
centerDatabase.patientDao().savePatient(patient1).test()

So my question is: is there a way to allow saving this entity without initializing this field to keep it null by default ? Or the only way would be to add a boolean to track if this field as been edited or not ?

Thanks


Solution

  • The Issue Your issue appears to be that the TypeConverter will not accept a null when converting a SessionsSettings.

    The Fix

    Change to use someObject: SessionSettings? i.e. added the ?

    Working Example/Demonstration

    The following demonstrates the subtle change using code that is based upon your code i.e.:-

    data class SessionSettings(
        val ssname: String,
        val ssine: Int
    )
    
    @Entity
    data class Patient(
        @PrimaryKey
        val patientId: Long?=null,
        val patientName: String?=null,
        var sessionSettings: SessionSettings?,   // the new field
        /// ... other fields
    )
    
    class SessionSettingsConverters {
        private var gson = Gson()
    
        @TypeConverter
        fun stringToSessionSettings(data: String?): SessionSettings? {
            if (data.isNullOrEmpty()) {
                return null
            }
            val sessionSettingsType = object : TypeToken<SessionSettings>() {}.type
            return gson.fromJson(data, sessionSettingsType)
        }
        @TypeConverter
        fun sessionSettingsToString(someObject: SessionSettings?): String? {
            return gson.toJson(someObject)
        }
    }
    
    @Dao
    interface SO75530351DAO {
        @Insert
        fun insert(patient: Patient): Long
        @Query("SELECT * FROM patient")
        fun getAllPatients(): List<Patient>
    }
    
    @TypeConverters(SessionSettingsConverters::class)
    @Database(entities = [Patient::class], exportSchema = false, version = 1)
    abstract class SO75530351DB: RoomDatabase() {
        abstract fun getDao(): SO75530351DAO
        companion object {
            private var instance: SO75530351DB?=null
            fun getInstance(context: Context): SO75530351DB {
                if (instance==null) {
                    instance=Room.databaseBuilder(context,SO75530351DB::class.java,"so75539351.db")
                        .allowMainThreadQueries()
                        .build()
                }
                return instance as SO75530351DB
            }
        }
    }
    

    And to use the above:-

        dbx = SO75530351DB.getInstance(this)
        daox = dbx.getDao()
    
        daox.insert(Patient(patientName = "Fred"))
        daox.insert(Patient(patientName = "Mary", sessionSettings = SessionSettings("SESSION1",10)))
        for(p in daox.getAllPatients()) {
            Log.d("DBINFO","Patient is ${p.patientName} ID is ${p.patientId} ${p.sessionSettings}")
        }
    

    The Log, after running:-

    D/DBINFO: Patient is Fred ID is 1 null
    D/DBINFO: Patient is Mary ID is 2 SessionSettings(ssname=SESSION1, ssine=10)
    

    The database, via App Inspection, :-

    enter image description here