Android数据库|青训营笔记

135 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第13天。

Android的sharePreferences只能存取简单类型,想要存储复杂数据类型,这就要借助数据库了。在Android开发中,内嵌了一个轻量型数据库SQLite

什么是SQLite

官方定义:SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。简单来说,是一个关系型数据库,类似于MySQL,可以执行SQL语句,但比其他关系型数据库更为轻量,使用更简单。

操作方法

定义数据库表

定义创建和删除表语句:

//定义建表语句
private const val SQL_CREATE_ENTRIES =
        "CREATE TABLE ${FeedEntry.TABLE_NAME} (" +
                "${BaseColumns._ID} INTEGER PRIMARY KEY," +
                "${FeedEntry.COLUMN_NAME_TITLE} TEXT," +
                "${FeedEntry.COLUMN_NAME_SUBTITLE} TEXT)"
//如果存在此表则先删除
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS ${FeedEntry.TABLE_NAME}"

定义SQLiteOpenHelper

定义一个类继承SQLiteOpenHelper,使它可以操作SQLite,重写onCreateonUpgrade方法

class FeedReaderDbHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    //数据库创建时调用
    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(SQL_CREATE_ENTRIES)
    }
    //数据库更新
    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL(SQL_DELETE_ENTRIES)
        onCreate(db)
    }
}

新增数据

在操作之前,需要实例化SQLiteOpenHelper子类,然后确定操作模式

val dbHelper = FeedReaderDbHelper(context)

// 用写的方式操作数据库
val db = dbHelper.writableDatabase

// 使用键值对操作数据
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
    put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle)
}

// 返回主键值 插入出错返回-1
val newRowId = db?.insert(FeedEntry.TABLE_NAME, null, values)

insert参数

  • 第一个参数为表名
  • 第二参数指定当ContentValues没有put任何值时应该执行哪些操作。如果指定的值是列名,则新插入一行,此列的值为null。如果指定值为null,就不插入任何行。
  • 第三个参数为要插入的ContentValues

数据读取

使用query方法读取,返回Cursor游标对象。

//使用读的方式操作数据库
val db = dbHelper.readableDatabase

// 想要查询的列
val projection = arrayOf(BaseColumns._ID, FeedEntry.COLUMN_NAME_TITLE, FeedEntry.COLUMN_NAME_SUBTITLE)

// 条件选择
val selection = "${FeedEntry.COLUMN_NAME_TITLE} = ?"
val selectionArgs = arrayOf("My Title")

// 返回值排序方式
val sortOrder = "${FeedEntry.COLUMN_NAME_SUBTITLE} DESC"

val cursor = db.query(
        FeedEntry.TABLE_NAME,   // 查询的表
        projection,             // 返回查询列
        selection,              // 条件列
        selectionArgs,          // 条件值
        null,                   // group by
        null,                   // don't filter by row groups
        sortOrder               // order by
)

游标Cursor开始会指向-1位置,使用moveToNext方法移动到下一行,直到最后一行。使用getLong方法或者getString方法提取列位置值,使用getColumnIndex或者getColumnIndexThrow获取列位置。

val itemIds = mutableListOf<Long>()
with(cursor) {
    while (moveToNext()) {
        val itemId = getLong(getColumnIndexOrThrow(BaseColumns._ID))
        itemIds.add(itemId)
    }
}
cursor.close()

数据删除

使用delete方法删除数据库行,返回删除行数。

val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
val selectionArgs = arrayOf("MyTitle")
val deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs)

数据更新

使用update方法进行更新,返回更新行数。

val db = dbHelper.writableDatabase
val title = "MyNewTitle"
val values = ContentValues().apply {
    put(FeedEntry.COLUMN_NAME_TITLE, title)
}

val selection = "${FeedEntry.COLUMN_NAME_TITLE} LIKE ?"
val selectionArgs = arrayOf("MyOldTitle")
val count = db.update(
        FeedEntry.TABLE_NAME,
        values,
        selection,
        selectionArgs)

Room框架

由上可知,SQLLite代码编写较为繁琐,需要使用的方法很多,因此Google推出了ORM框架,也就是Room

Room操作

导入依赖

在Module的build.gradle文件中编写:

implementation "androidx.room:room-runtime:$versions.roomVersion"
implementation "androidx.room:room-ktx:$versions.roomVersion"
kapt "androidx.room:room-compiler:$versions.roomVersion"

新建实体类

实体类属性可包含一个表的列:

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
 
@Entity(tableName = "Word") //对应的是数据库中的表名称
class Word() {
    //主键,自增
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0
 
    //不使用ColumnInfo,则使用字段名。这里name的值就是数据表的字段名。
    @ColumnInfo(name = "english_word")
    var word: String = ""
 
    @ColumnInfo(name = "chinese_meaning")
    var chineseMeaning: String = ""
 
    override fun toString(): String {
        return "Word(id=$id, word='$word', chineseMeaning='$chineseMeaning')"
    }
 
    constructor(_word: String, _chineseMeaning: String) : this() {
        word = _word
        chineseMeaning = _chineseMeaning
    }
}

新建数据库接口

类似于java中编写Dao层,使用注解简化开发

import androidx.room.*
 
@Dao
interface WordDao {
    @Insert
    fun insertWords(vararg item: Word)
 
    @Update
    fun updateWords(vararg item: Word)
 
    @Delete
    fun deleteWords(vararg item: Word)
 
    @Query("DELETE FROM Word")
    fun deleteAllWords()
 
    @Query("SELECT * FROM Word ORDER BY id DESC")
    fun getAllWords(): List<Word>
}

@Query注解不仅限于查找,也能编写自定义SQL语句,可以用来执行特殊SQL操作。

创建DataBase抽象类

新建一个AppDatabase 类,继承自 RoomDatabase 类,添加 @Database 注解,在其中声明版本号,包含的实体类。并在抽象类中声明获取 Dao 类的抽象方法。

@Database(version = 1, entities = [User::class])
abstract class AppDatabase : RoomDatabase() {
    
    abstract fun wordDao(): wordDao

    companion object {
        private var instance: AppDatabase? = null
        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            return instance?.let { it }
                ?: Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                    .build()
                    .apply { instance = this }
        }
    }
}

在 getDatabase 方法中,第一个参数务必要使用 applicationContext,以防止内存泄漏,第三个参数表示数据库的名字。

总结

Android开发其实还有很多类似的数据库框架,这里只介绍了Google自己的框架,在实际的应用中,可以根据业务需要选择合适的开发框架。