如何保存和检索一个日期与Kotlin在android studio使用sqlite?

o3imoua4  于 2023-03-13  发布在  SQLite
关注(0)|答案(1)|浏览(162)

我需要帮助请?我正在使用Kotlin与sqlite日期操作工作,我想知道如何做,如果用户点击按钮,我给他一个日历或他只选择一个日期(没有时间),我得到这个日期保存在sqlite中的文本字段,然后从sqlite检索这个字段,并显示在文本视图中,例如?谢谢
myTable

d6kp6zgx

d6kp6zgx1#

可以用多种格式存储日期。最有效的空间/存储方式是将其存储为整数类型值(通常为long)。然而,如果只存储日期,则可以通过删除时间并只存储表示日期的整数来提高效率。

  • 在下面的示例中,使用YYYY-MM-DD存储为文本的日期将不占用10个字节,而是最多存储4个字节。(请参阅https://www.sqlite.org/datatype3.html#storage_classes_and_datatypes)

下面是一个基于部分代码的示例,用于存储通过LocalDate对象获取的日期:
首先是扩展SQLiteOpenHelper类的类,通常称为数据库助手,因此称为DBHelper:-

const val DATABASE_NAME = "the_database.db"
const val DATABASE_VERSION = 1
const val TABLE_RESERVATION = "reservation"
const val COLUMN_RESERVATION_ID = "id"
const val COLUMN_RESERVATION_DATE = "date_reservation"
private const val CREATE_TABLE_RESERVATION = "CREATE TABLE IF NOT EXISTS ${TABLE_RESERVATION} (${COLUMN_RESERVATION_ID} INTEGER PRIMARY KEY, ${COLUMN_RESERVATION_DATE} INTEGER);"
/* etc */
class DBHelper(context: Context): SQLiteOpenHelper(context, DATABASE_NAME,null, DATABASE_VERSION) {
    override fun onCreate(p0: SQLiteDatabase) {
        p0.execSQL(CREATE_TABLE_RESERVATION)
    }

    override fun onUpgrade(p0: SQLiteDatabase?, p1: Int, p2: Int) {
        TODO("Not yet implemented")
    }

    fun insertReservation(reservationId: Long?, reservationDate: LocalDate): Long {
        val cv = ContentValues()
        if (reservationId!= null) {
            cv.put(COLUMN_RESERVATION_ID,reservationId)
        }
        cv.put(COLUMN_RESERVATION_DATE,reservationDate.toEpochDay())
        return this.writableDatabase.insert(TABLE_RESERVATION,null,cv)
    }
    fun insertReservation(reservationDate: LocalDate): Long = insertReservation(null,reservationDate)

    fun updateReservationDate(reservationId: Long,reservationDate: LocalDate): Int {
        val cv = ContentValues()
        cv.put(COLUMN_RESERVATION_DATE,reservationDate.toEpochDay())
        return this.writableDatabase.update(TABLE_RESERVATION,cv,"${COLUMN_RESERVATION_ID}=?", arrayOf(reservationId.toString()))
    }

    companion object {
        @Volatile
        var instance: DBHelper?=null
        fun getInstance(context: Context): DBHelper {
            if (instance==null) {
                instance=DBHelper(context)
            }
            return instance as DBHelper
        }
    }
}
  • 这包括用于插入的函数(实际上是两个,一个具有较短签名,用于生成ID而不是传递ID)
  • 并且它包括可以根据ID更新现有行的函数
  • getInstance函数允许使用DBHelper的单个示例。

为了演示上述内容,下面是一些插入多行的活动代码:-

class MainActivity : AppCompatActivity() {
    lateinit var dbHelper: DBHelper
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        dbHelper=DBHelper.getInstance(this)

        val date1 = LocalDate.parse("2023-01-01")
        val date2 = LocalDate.parse("2023-03-01")
        val date3 = LocalDate.parse("2023-04-04")
        dbHelper.insertReservation(date1)
        dbHelper.insertReservation(100,date2)
        dbHelper.insertReservation(date3)
        dbHelper.insertReservation(111, LocalDate.parse("2023-05-05"))
        dbHelper.updateReservationDate(2, LocalDate.parse("2020-03-01"))

    }
}

运行上述程序后,使用App Inspection,结果数据为:-

  • 请注意如何使用AUTOINCREMENT生成ID为1和101的行。AUTOINCREMENT的效率很低,如 “AUTOINCREMENT关键字会增加额外的CPU、内存、磁盘空间和磁盘I/O开销,如果不是严格需要,则应避免使用。通常不需要。"
  • 参见https://www.sqlite.org/autoinc.html

因此,您可能会质疑数字的有效性,因此使用App Inspection运行查询:-SELECT date(date_reservation * 24 * 60 * 60,'unixepoch') AS formattedDate,* FROM reservation,然后结果确认预期日期:-

当要检索日期时,有很多选择,你可以使用SQLite的日期和时间函数 (参见https://www.sqlite.org/lang_datefunc.html,或者你可以检索存储值(可以用很多方式存储)。
作为一个例子,继续上面的内容,考虑这个函数(添加到DBHelper类中)和const(包含在函数之前):

const val DATEONLY_TO_YYYYMMDD = 24 * 60 * 60
private val dateInYYYYMMDDFormat = "date(${COLUMN_RESERVATION_DATE} * ${DATEONLY_TO_YYYYMMDD},'unixepoch')"
fun getReservations(reservationId: Long?= null,startAndEndDateAsList: List<LocalDate>?=null): Cursor {
    val whereClause = StringBuilder()
    val whereArgs = arrayListOf<String>()
    if (reservationId != null) {
        whereClause.append("${COLUMN_RESERVATION_ID}=?")
        whereArgs.add(reservationId.toString())
    }
    if (startAndEndDateAsList !=null && startAndEndDateAsList.size ==2){
        if (whereClause.isNotEmpty()) whereClause.append(" AND ")
        whereClause.append("${COLUMN_RESERVATION_DATE} BETWEEN ? AND ?")
        var firstIdx=0
        var secondIdx = 1
        if (startAndEndDateAsList[0] > startAndEndDateAsList[1]) {
            firstIdx=1
            secondIdx=0
        }
        whereArgs.add(startAndEndDateAsList[firstIdx].toEpochDay().toString())
        whereArgs.add(startAndEndDateAsList[secondIdx].toEpochDay().toString())
    }
    return this.writableDatabase.query(TABLE_RESERVATION,
        arrayOf("*","${dateInYYYYMMDDFormat} AS formattedDate"),
        whereClause.toString(),
        whereArgs.toList().toTypedArray(),
        null,null,null
    )
}

首先,Cursor对象是使用SQLiteDatabase进行查询的输出。游标将具有0-nnnnnn行。您可以使用move???函数(例如moveToFirstmoveToNext....)导航游标。您可以使用get???函数从列中检索数据。
在上面的输出列是ALL列(
*
(实际上更准确地说是所有未隐藏的列),还有一个名为(别名)formattedDate的派生列。

  • 这用于演示存储值和使用SQLite日期函数格式化的值(见上文)。

该函数非常灵活,允许提取所有行,根据id列的值和/或根据日期范围提取行(单行)。

  • 注意,所提供的日期范围被相应地调整以适合BETWEEN子句(即,第一个值必须是最小值(如果两者相同,则顺序无关紧要))。

DBHelper类中的新函数一起使用的是“活动”中的以下附加代码(插入数据之后):-

DatabaseUtils.dumpCursor(dbHelper.getReservations(null, listOf(LocalDate.parse("1900-01-01"),LocalDate.parse("2024-01-01"))))
  • dumpCursor是一个将游标内容输出到日志的实用程序。在本例中,传递给**getReservations**函数的参数指定:-
  • 将提取任何/所有id/s(即null表示WHERE子句不测试id列)
  • 将包含BETWEEN子句。

输出(卸载应用程序并重新运行后)为:-

2023-03-09 12:34:30.531 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@c64b406
2023-03-09 12:34:30.532 I/System.out: 0 {
2023-03-09 12:34:30.532 I/System.out:    id=1
2023-03-09 12:34:30.532 I/System.out:    date_reservation=19358
2023-03-09 12:34:30.532 I/System.out:    formattedDate=2023-01-01
2023-03-09 12:34:30.532 I/System.out: }
2023-03-09 12:34:30.532 I/System.out: 1 {
2023-03-09 12:34:30.532 I/System.out:    id=100
2023-03-09 12:34:30.532 I/System.out:    date_reservation=19417
2023-03-09 12:34:30.532 I/System.out:    formattedDate=2023-03-01
2023-03-09 12:34:30.532 I/System.out: }
2023-03-09 12:34:30.532 I/System.out: 2 {
2023-03-09 12:34:30.532 I/System.out:    id=101
2023-03-09 12:34:30.532 I/System.out:    date_reservation=19451
2023-03-09 12:34:30.532 I/System.out:    formattedDate=2023-04-04
2023-03-09 12:34:30.532 I/System.out: }
2023-03-09 12:34:30.533 I/System.out: 3 {
2023-03-09 12:34:30.533 I/System.out:    id=111
2023-03-09 12:34:30.533 I/System.out:    date_reservation=19482
2023-03-09 12:34:30.533 I/System.out:    formattedDate=2023-05-05
2023-03-09 12:34:30.533 I/System.out: }
2023-03-09 12:34:30.533 I/System.out: <<<<<
  • 可以看出,所存储的值和导出的格式化日期是所期望的。
  • 有一点需要注意/记住/遵守,那就是之前链接的日期和时间函数文档,例如,修饰符unixepoch在上面的文档中扮演了一个角色,如果在处理上面的数据时不使用它,那么结果将不会如所期望的那样。但是,这些函数的日期和时间处理能力非常强大/有用。

下面是参数的其他排列的一些示例(所有参数都返回预期结果):-

DatabaseUtils.dumpCursor(dbHelper.getReservations())
    DatabaseUtils.dumpCursor(dbHelper.getReservations( startAndEndDateAsList =  listOf( LocalDate.parse("2024-01-01"),LocalDate.parse("1900-01-01"))))
    DatabaseUtils.dumpCursor(dbHelper.getReservations(reservationId = 100))

相关问题