首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建一个TypeConverter,将LocalDate转换为Room可以理解/保存的格式?

如何创建一个TypeConverter,将LocalDate转换为Room可以理解/保存的格式?
EN

Stack Overflow用户
提问于 2021-04-15 12:59:26
回答 2查看 1.2K关注 0票数 3

我正在使用Room。我需要指导如何将LocalDatejava.time转换成一种格式(可能是LongTimeStampsql.Date,或者我甚至不知道还有什么),这样我就可以将日期保存到数据库中。

我有一个书实体:

代码语言:javascript
复制
@Entity
data class Book(
    @ColumnInfo(name = "date") val date: LocalDate,  
    @ColumnInfo(name = "count") val count: Int,
    @PrimaryKey(autoGenerate = true)
    val id: Int? = null
)

我还创建了图书刀:

代码语言:javascript
复制
@Dao
interface BookDao {
    @Query("SELECT * FROM book WHERE :date >= book.date")
    fun getBooks(date: LocalDate): List<Book>
}

现在,我不知道如何创建将LocalDate转换为。。。(再说一次,我甚至不知道我应该转换我的LocalDate。是LongTimeStampsql.Date还是其他任何东西)。

认为应该将LocalDate转换为sql.Date,以便Room可以保存它。所以,我创建了这样的转换器:

Converters.kt

代码语言:javascript
复制
class Converters {
    @TypeConverter
    fun convertLocalDateToSqlDate(localDate: LocalDate?): Date? {
        return localDate?.let { Date.valueOf(localDate.toString()) }
    }

    @TypeConverter
    fun convertSqlDateToLocalDate(sqlDate: Date?): LocalDate? {
        val defaultZoneId = systemDefault()
        val instant = sqlDate?.toInstant()
        return instant?.atZone(defaultZoneId)?.toLocalDate()
    }
}

但是,我错了:

代码语言:javascript
复制
error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.
    private final java.time.LocalDate date = null;
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-04-16 04:36:32

您面临的问题是,TypeConverter应该从/转换为Room支持的用于插入/提取数据的一组特定类型。

比较常见的有Int、Long Byte、Byte[]、Float、Double、Decimal、String、BigInt、BigDecimal。

  • 可能还有其他类型,但是可以使用的类型是有限的。

因此,该消息是说,请转换您的LocalDate或日期,因为它无法处理它们。

我建议将日期存储为unix日期(除非您想要获得微秒的准确性,当您可以用毫秒存储时间时(这在sqlite中使用日期/时间函数时可能会有点尴尬,可能需要转换),即长时间或Int )。

在这样做时,你:

  • 将最大限度地减少用于存储值的存储量,
  • 将能够利用SQLite日期和时间函数
  • 很容易将它们提取成非常灵活的格式,
  • 您可以按原样提取它们,然后使用类函数进行格式转换,
  • 对于上述情况,您将不需要TypeConverters。

下面是一个示例,展示了一些功能,将它们存储为unix时间戳

首先,您的Book实体的一个版本使用日期的默认值:-

代码语言:javascript
复制
@Entity
data class Book(
    @ColumnInfo(name = "date", defaultValue = "(strftime('%s','now','localtime'))")
    val date: Long? = null,
    @ColumnInfo(name = "count")
    val count: Int,
    @PrimaryKey(autoGenerate = true)
    val id: Int? = null
)
  • 请参阅SQLITE日期和时间函数来解释上面的内容。简而言之,上面的内容允许您插入一行,并将日期和时间设置为当前日期时间。
  • 请参见insertBook函数,用于只使用所提供的计数进行插入。

随书实体书刀:-

代码语言:javascript
复制
@Dao
interface BookDao {
    @Insert /* insert via a book object */
    fun insert(book: Book): Long
    @Query("INSERT INTO BOOK (count) VALUES(:count)")

    /* insert supplying just the count id and date will be generated */
    fun insertBook(count: Long): Long
    @Query("SELECT * FROM book")

    /* get all the books  as they are stored into a list of book objects*/
    fun getAllFromBook(): List<Book>

    /* get the dates as a list of dates in yyyy-mm-dd format using the SQLite date function */
    @Query("SELECT date(date, 'unixepoch', 'localtime') AS mydateAsDate FROM book")
    fun getDatesFromBook(): List<String>

    /* get the date + 7 days */
    @Query("SELECT date(date,'unixepoch','localtime','+7 days') FROM book")
    fun getDatesFromBook1WeekLater(): List<String>
}
  • 见评论(以及活动和结果)

活动(非常标准的BookDatabase类,所以不包括在内)

代码语言:javascript
复制
class MainActivity : AppCompatActivity() {
    lateinit var db: BookDatabase
    lateinit var dao: BookDao
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        db = BookDatabase.getInstance(this)
        dao = db.getBookDao()

        /* Insert two rows */
        dao.insert(Book(System.currentTimeMillis() / 1000,10)) /* don't want millisecs */
        dao.insertBook(20) /* use the date columns default value (similar to the above) */

        /* get and log the date objects */
        for(b: Book in dao.getAllFromBook()) {
            Log.d("BOOKINFO_BOOK", " Date = ${b.date} Count = ${b.count} ID = ${b.id}")
        }

        /* get the dates in yyy-mm-dd format */
        for(s: String in dao.getDatesFromBook()) {
            Log.d("BOOKINFO_DATE",s)
        }

        /* get the dates but with a week added on */
        for(s: String in dao.getDatesFromBook1WeekLater()) {
            Log.d("BOOKINFO_1WEEKLATER",s)
        }
    }
}

运行后的日志:

代码语言:javascript
复制
2021-04-16 14:28:50.225 D/BOOKINFO_BOOK:  Date = 1618547330 Count = 10 ID = 1
2021-04-16 14:28:50.225 D/BOOKINFO_BOOK:  Date = 1618583330 Count = 20 ID = 2
2021-04-16 14:28:50.226 D/BOOKINFO_DATE: 2021-04-16
2021-04-16 14:28:50.227 D/BOOKINFO_DATE: 2021-04-17
2021-04-16 14:28:50.228 D/BOOKINFO_1WEEKLATER: 2021-04-23
2021-04-16 14:28:50.228 D/BOOKINFO_1WEEKLATER: 2021-04-24
  • (请注意,对当地时间的调整(尽管它使用不当,因而向前跳))
  • 存储在Book对象和数据库中的前2行数据
  • 接下来的2行,由SQLite日期/时间函数转换为YYYY格式的日期。
  • 最后2行存储日期转换为YYYY-MM-DD后一周的日期。

根据数据库检查员提供的数据库:-

票数 3
EN

Stack Overflow用户

发布于 2021-04-16 09:32:12

如果您想使用转换器,您可以按照文档的方式。在您的示例中,您尝试转换为SQLdate,但Android建议使用Long。

代码语言:javascript
复制
class Converters {
  @TypeConverter
  fun fromTimestamp(value: Long?): Date? {
    return value?.let { Date(it) }
  }

  @TypeConverter
  fun dateToTimestamp(date: Date?): Long? {
    return date?.time?.toLong()
  }
}

然后将@TypeConverters注释添加到AppDatabase。

对于LocalDate,应该是这样的:

代码语言:javascript
复制
class Converters {
    @TypeConverter
    fun fromTimestamp(value: Long?): LocalDate? {
        return value?.let { LocalDate.ofEpochDay(it) }
    }

    @TypeConverter
    fun dateToTimestamp(date: LocalDate?): Long? {
        val zoneId: ZoneId = ZoneId.systemDefault()
        return date?.atStartOfDay(zoneId)?.toEpochSecond()
    }
}

来源

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67108957

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档