15

Detailed log

error: Cannot figure out how to save this field into database. You can 
consider adding a type converter for it.
private final java.util.Date mTime = null;

I have an entity with a field as

var mStartTime : Date = Date() // java.util.Date

Why cant Room persist Date objects? What can be best converter for Date?

double-beep
  • 5,031
  • 17
  • 33
  • 41
Arka Prava Basu
  • 2,366
  • 3
  • 18
  • 34
  • Are you sure that you provided a `DateTypeConverter`? If yes please post it along with your question. – Enzokie May 25 '18 at 05:41

4 Answers4

33

Date is exactly the example given in https://developer.android.com/training/data-storage/room/referencing-data.

For example, if we want to persist instances of Date, we can write the following TypeConverter to store the equivalent Unix timestamp in the database:

public class Converters {
    @TypeConverter
    public static Date fromTimestamp(Long value) {
        return value == null ? null : new Date(value);
    }
    @TypeConverter
    public static Long dateToTimestamp(Date date) {
        return date == null ? null : date.getTime();
    }
}

The preceding example defines 2 functions, one that converts a Date object to a Long object and another that performs the inverse conversion, from Long to Date. Since Room already knows how to persist Long objects, it can use this converter to persist values of type Date.

Next, you add the @TypeConverters annotation to the AppDatabase class so that Room can use the converter that you've defined for each entity and DAO in that AppDatabase:

AppDatabase.java

@Database(entities = {User.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

A side note: java.util.Date is considered to be badly designed (and java.util.Calendar is much worse). If you have any non-trivial date-time logic and can get away with API level 26 (Java 8 on desktop), it's generally better to use java.time package. And if you can't, see https://github.com/JakeWharton/ThreeTenABP for a backport.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487
  • Hi @Alexey Romanov , could you please elaborate why the java.util.Date and Calendar libraries are bad or shouldn't be used. Thanks for the answer on TypeConverters, btw. – user3760100 Oct 31 '18 at 17:48
  • 1
    @user3760100 See e.g. https://stackoverflow.com/questions/1969442/whats-wrong-with-java-date-time-api, https://codeblog.jonskeet.uk/2017/04/23/all-about-java-util-date/, https://javarevisited.blogspot.com/2017/04/5-reasons-why-javas-old-date-and-Calendar-API-bad.html. – Alexey Romanov Nov 01 '18 at 11:38
  • Thanks for the links! – user3760100 Nov 03 '18 at 18:25
  • 3
    Or instead of using this crap in your code, just use a long for your field and when you go to set the field, just get the time from your Date object. Seriously Google?? WTF? This is 2019 and you can't even bother to add built-in support for Dates? – Johann May 29 '19 at 15:44
  • It's not like Room understands how to persist a java.time.LocalDate or LocalDateTime object either... it's so ridiculous that the incredibly basic concept of storing dates/datetimes in a sql database is something that I need to copy/paste some extra code for. I just don't understand why this isn't just built into Room. Ok, don't want to support old java.util.Date in Room? Fine, but at least support the new java.time.LocalDate. But no, sigh. Apple's CoreData inherently understands Dates, like every good ORM should... but not the "cutting edge" Room! – Kenny Wyland Aug 08 '23 at 21:26
2
    // Java code will not convert to Kotlin very 
    // well so here is the Kotlin: Converter 
    // class

    public class Converters {
        @TypeConverter
        fun fromTimestamp( value: Long?) : 
                       java.sql.Date {
            return java.sql.Date(value ?: 0)
        }
        @TypeConverter
        fun dateToTimestamp(date :java.sql.Date?) 
                                 :Long {
            return date?.getTime() ?: 0
       }

    // Here is the type converters example in 
    // Kotlin
    @Database(entities = [DbNasaPictures::class], 
              version = 2)
    @TypeConverters(Converters::class)
    abstract class PicturesDatabase: 
                     RoomDatabase() {
BroPage
  • 129
  • 10
0

All above answers is for list of strings.But below helps you to find converter for list of your objects.

Just in place of "YourClassName" ,add your Object class.

 @TypeConverter
        public String fromValuesToList(ArrayList<**YourClassName**> value) {
            if (value== null) {
                return (null);
            }
            Gson gson = new Gson();
            Type type = new TypeToken<ArrayList<**YourClassName**>>() {}.getType();
            return gson.toJson(value, type);
        }
    
        @TypeConverter
        public ArrayList<**YourClassName**> toOptionValuesList(String value) {
            if (value== null) {
                return (null);
            }
            Gson gson = new Gson();
            Type type = new TypeToken<List<**YourClassName**>>() {
            }.getType();
            return gson.fromJson(value, type);
        }
Tarun Anchala
  • 2,232
  • 16
  • 15
  • It gives YourClassName "error: incompatible types: NonExistentClass cannot be converted to Annotation @error.NonExistentClass()" error – TejpalBh Mar 15 '22 at 13:55
0

In Kotlin, Need to add @TypeConverters in Two files

  1. Database class in which extend by RoomDatabase
@Database(entities = [ToDo::class], version = 1, exportSchema = false)
@TypeConverters(DateConverter::class)
abstract class AppDatabase : RoomDatabase() {

    abstract fun todoDao(): ToDoDao

    companion object {
        @Volatile private var instance: AppDatabase? = null

        fun getDatabase(context: Context): AppDatabase =
            instance ?: synchronized(this) { instance ?: buildDatabase(context).also { instance = it } }

        private fun buildDatabase(appContext: Context) =
            Room.databaseBuilder(appContext, AppDatabase::class.java, "todo")
                .fallbackToDestructiveMigration()
                .build()
    }

}
  1. Data class in which @Entity declared and need to add DateConverter class for Date
@Entity(tableName = "todo")
data class ToDo(
    @PrimaryKey
    val id: Int,
    val title: String,
    val description: String,
    val time: String,
    val date: String,
    val types: Int,
    @TypeConverters(DateConverter::class)
    val date_time: Date,
    val created: String
)

DateConverter class

import androidx.room.TypeConverter
import java.util.*

class DateConverter {

    @TypeConverter
    fun toDate(timestamp: Long): Date {
        return Date(timestamp)
    }

    @TypeConverter
    fun toTimestamp(date: Date): Long {
        return date.time
    }
}
Pratik Dodiya
  • 2,337
  • 1
  • 19
  • 12