So I'm struggling to understand how I can create a simple type converter for a custom class compromising of 2 Strings so I can use it with Room db. Nothing I read makes sense.
What will my Converter class look like to handle MoreStrings and solve the str3 error: Cannot figure out how to save this field into database. You can consider adding a type converter for it?
data class MyString (
val str1: String = "",
val str2: String = "",
val str3: MoreStrings
) : Serializable
data class MoreStrings (
val str4: String = "",
val str5: String = ""
) : Serializable
The need for Type converters is due to how SQLite, the database around which Room is a wrapper, stores data in a finite number of types (storage types) INTEGER, REAL, TEXT, BLOB and NULL.
Room can determine which storage type to utilise for a larger but still predefined set of types. Int, Long, Byte, Boolean .... as INTEGER, Float, Double .... as REAL, ByteArray .... as BLOB and String as TEXT.
When saving an Object that is not such a predefined type, the object can be saved in a suitable storage class, as a single such value, along with the ability to also extract the data. Hence the use of TypeConverters and the suggestion for their use.
In your case , assuming that MyString is @Entity
annotated, and thus each field is saved as a column in the table. Then str1 and str2 needs nothing special as the values are strings and can be stored as TEXT.
However, str3, is a type of MyString and thus not of a predefined type.
So a method is required to either store the MyString as a single column or to split the MyString into more columns.
The first is where TypeConvereters come into play. They must be in pairs, one to convert the object into a single value, for storing the data in the database. The other to build the object from the single value for retrieving the data from the database.
As an example (a simple example)for MoreStrings you could have a type converter that simply concatenates str4 and str5 into a single String with a suitable separator allowing the singlke String to be split into the 2 Strings that would be required to build a MoreStrings object.
e.g. you could have:-
class MyTypeConverter {
@TypeConverter
fun convertFromMoreStringsToString(moreStrings: MoreStrings): String {
return moreStrings.str4 + "::" + moreStrings.str5
}
@TypeConverter
fun convertToMoreStringsFromString(stringFromDB: String) : MoreStrings {
val split = stringFromDB.split("::")
return MoreStrings(split[0],split[1])
}
}
@TypeConverter
annotation.Room has to know about the converters to be able to use them. To define the converters to Room you use the @TypeConverters
annotation (note plural), with the classes specified as the varargs parameter of the annotation.
e.g. for the above you could have:-
@TypeConverters(MyTypeConverter::class)
@Database
level.Typically as you are often dealing with objects, then GSON libraries are used to convert the object from and into a JSON string.
There is fact no need for TypeConverters, the underlying data can always be broken down into data that can be stored as one.
The @Embedded
annotation can be used to embed the fields of an object as individual columns e.g. you could have:-
@Entity
data class MyOtherString(
@PrimaryKey
val stra: String = "",
val strb: String = "",
@Embedded
val strc: MoreStrings
)
CREATE TABLE IF NOT EXISTS
MyOtherString (
straTEXT NOT NULL,
strbTEXT NOT NULL,
str4TEXT NOT NULL,
str5 TEXT NOT NULL, PRIMARY KEY(
stra))
i.e. columns str4 and str5 are for the embedded MoreStrings object.