这是我参与「第四届青训营 」笔记创作活动的第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,重写onCreate和onUpgrade方法
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自己的框架,在实际的应用中,可以根据业务需要选择合适的开发框架。