android - 如何创建将 LocalDate 转换为 Room 可以理解/保存的格式的 TypeConverter?

标签 android android-room java-time

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

我有一个图书实体:

@Entity
data class Book(
    @ColumnInfo(name = "date") val date: LocalDate,  
    @ColumnInfo(name = "count") val count: Int,
    @PrimaryKey(autoGenerate = true)
    val id: Int? = null
)

我还创建了 Book DAO:

@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

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()
    }
}

但是,我得到了错误:

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;

最佳答案

您遇到的问题是 TypeConverter 应该从/到 Room 支持插入/提取数据的一组特定类型之间进行转换。

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

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

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

我建议将日期存储为 unix 日期(除非你想要微秒精度,而你可以用毫秒存储时间(这在 sqlite 中使用日期/时间函数时可能有点尴尬并且可能需要转换))即作为长整数或整数。

这样做你:

  • 将最小化用于存储值的存储量,
  • 将能够利用 SQLite Date and Time functions ,
  • 可以轻松地将它们提取为非常灵活的格式,
  • 您可以将它们原样提取出来,然后使用类函数对它们进行格式转换,
  • 以上内容不需要 TypeConverter。

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

首先是使用默认日期的图书实体版本:-

@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 函数以仅使用提供的计数进行插入。

伴随书实体书道:-

@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>
}
  • 查看评论(以及 Activity 和结果)

Activity (非常标准的 BookDatabase 类,因此不包括在内)

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)
        }
    }
}

运行后的日志:-

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-MM-DD 格式
  • 最后 2 行存储日期转换为 YYYY-MM-DD 后一周的日期

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

enter image description here

关于android - 如何创建将 LocalDate 转换为 Room 可以理解/保存的格式的 TypeConverter?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67108957/

相关文章:

android - SwipeRefreshLayout 在将支持库更新到 23.2.0 后阻止 AppBarLayout 向下滚动并显示刷新圈

java - Room - 比较实体中的列表

java - java.time.LocalDate 导入失败

java - 格式化程序和不带年份的本地化 LocalDate (JSR-310)

java - PostgreSQL 日期转换为 Java Date 而不是 LocalDate

Android:如何在 RelativeLayout 中均匀分布组件?

Android:当应用程序处于纵向时,以横向显示 View

android - 如何使用 Assets 文件夹中的 CSS 文件加载 URL?

android - 在协程 GlobalScope 上运行 Room Persistence Library 查询

安卓房间数据库。以表名作为参数创建和删除表