安卓系统中SQL Delight与Room数据库的比较
在选择移动数据库框架时,有几种选择。我们有Room持久性库、Realm数据库和SQL Delight。
前提条件
要继续学习,你应该具备以下条件
- 在你的机器上安装了Android Studio。
- 具有创建和运行Android应用程序的良好知识。
- 对Kotlin编程语言和View绑定有基本了解。
- 对Dagger Hilt的依赖注入有基本了解
- 对ViewModels和Coroutines有一些基本了解。
目标
在本教程结束时,读者将。
- 了解什么是SQL Delight。
- 理解SQL Delight的优点和缺点。
- 在Android项目中使用SQL Delight。
什么是SQL愉悦数据库?
SQL delight是一个数据库框架,就像Room库一样。它是跨平台的,从给定的SQL语句中生成类型安全的类。
它在编译时检查数据库模式、迁移和SQL语句。
使用SQL delight的优点
- 它能从不同的SQL语句中生成类型安全的代码和类。
- 它与Kotlin多平台(KMM)兼容,意味着我们可以在IOS和Android中使用它。
- 在处理多表数据库时,SQL Delight的效果更好。
使用SQL喜悦的缺点
- 与Room数据库库相比,我们必须要写更多的SQL代码。
开始使用SQL Delight
在本教程中,我们将用SQL Delight库创建一个简单的安卓笔记应用程序。
第1步 - 创建一个安卓项目
启动Android Studio并创建一个空的Android项目,如下图所示。

第2步 - 设置项目
在这一步,我们将为我们的项目做所有必要的设置。
在你的应用级build.gradle 文件中添加以下插件。
id 'com.squareup.sqldelight'
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
还是在你的应用级build.gradle 文件中,添加以下依赖项。
// SQL Delight
implementation "com.squareup.sqldelight:android-driver:1.5.2"
implementation "com.squareup.sqldelight:coroutines-extensions-jvm:1.5.2"
// Coroutines
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
// Dagger - Hilt
implementation "com.google.dagger:hilt-android:2.38.1"
kapt "com.google.dagger:hilt-android-compiler:2.38.1"
implementation 'androidx.hilt:hilt-navigation-fragment:1.0.0'
// ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
在你的项目级build.gradle 文件中,添加以下classpaths。
classpath 'com.squareup.sqldelight:gradle-plugin:1.5.2'
classpath "com.google.dagger:hilt-android-gradle-plugin:2.38.1"
然后我们需要在Android Studio中安装SQLDelight插件。它可以从SQL生成类型安全的Kotlin APIs,并在IDE中提供SQL的语言功能。
打开你的Android Studio " 设置 " 插件,然后点击市场,搜索该插件。

第3步 - 定义数据库
在我们的应用程序级build.gradle ,我们需要添加一些设置,通知SQL Delight应该在哪里生成我们的数据库,以及它应该如何命名。
在sqldelight 块中,我们定义数据库的名称,并传递应用程序的包名,指定数据库的归属。
dependencies {
...
}
sqldelight {
NotesDatabase {
packageName = "com.sheecodes.sqldelightdemo"
}
}
第4步 - 定义SQL查询
首先,从Android视图切换到项目视图。由于定义我们的SQL查询的文件需要在主源集中定义,我们将在main 包中添加SQL喜悦文件。
右键单击main 包,创建一个名为sqldelight 的目录,然后添加另外两个目录,即sqldelight/demo/notesdb 。
右键单击notesdb 目录,使用SQLDelight 插件,创建一个SQL Delight表。
输入我们表的名称notesEntity ,并从选项中选择table 。

它应该产生像这样的东西。
CREATE TABLE notesEntity(
);
你在SQL中定义表的方式在SQL Delight中也是一样的。我们可以继续在我们的表中添加一个id,title, 和description 。
CREATE TABLE notesEntity(
id INTEGER NOT NULL PRIMARY KEY,
title TEXT NOT NULL,
description TEXT NOT NULL
);
SQL Delight将使用这个代码为我们生成一个类型安全的kotlin代码。
要包括更多的查询或表,你可以定义一个新的SQL文件,或者只是在现有的文件下面添加它们。
在我们的表下面,我们将定义我们的应用程序将使用的CRUD操作,即插入、读取、更新和删除。
我们首先写出函数名称,然后写出函数被调用时应该执行的SQL语句。
getNoteById: SELECT * FROM notesEntity WHERE id = :id;
getAllNotes: SELECT * FROM notesEntity;
insertNote: INSERT OR REPLACE INTO notesEntity VALUES (?,?,?);
deletePersonById: DELETE FROM notesEntity WHERE id = :id;
现在我们应该重建项目,以便SQL Delight能够生成相应的类。
第5步 - 定义数据源
首先,从项目视图切换到Android视图。在你的根包中定义一个新的包,名为data 。
在新的目录中,定义一个Interface ,并将其命名为NoteDataSource 。这个接口将包含帮助我们与数据库交互的函数。
interface NoteDataSource {
suspend fun insertNote(title: String, description: String,id: Long? = null)
fun getAllNotes(): Flow<List<NotesEntity>>
suspend fun getNoteById(id: Long): NotesEntity?
suspend fun deleteNoteById(id: Long)
}
第一个函数,getAllNotes ,返回一个NotesEntity ,这是一个由SQLDelight在幕后生成的类。
尽管如此,在data 包内,我们需要创建一个NoteDataSource 的实现。创建一个新的Kotlin类NoteDataSourceImpl ,它扩展了NoteDataSource 。
该类将在其构造函数中接受对我们的数据库--NotesDatabase 的引用作为其参数。
你可以记得在我们的应用级build.gradle ,我们定义了我们的数据库,然后由SQLDelight生成。
class NoteDataSourceImpl(db: NotesDatabase) : NoteDataSource {
...
}
在这个类中,我们定义了一个对象,它包含对我们的notesEntity 查询的引用。
private val queries = db.notesEntityQueries
然后我们在该类中添加以下函数的实现。
插入一个笔记
override suspend fun insertNote(title: String, description: String, id: Long?) {
return withContext(Dispatchers.IO) {
queries.insertNote(id, title, description)
}
}
获取所有笔记
override fun getAllNotes(): Flow<List<NotesEntity>> {
return queries.getAllNotes().asFlow().mapToList()
}
由于我们要返回一个笔记列表的Flow ,所以在SQLDelight中有一个函数叫做asFlow() ,我们可以在查询的最后追加这个函数。尽管如此,还是会有一个错误,我们只需追加mapToList() 来解决这个问题。
通过ID获得一个笔记
override suspend fun getNoteById(id: Long): NotesEntity? {
return withContext(Dispatchers.IO) {
queries.getNoteById(id).executeAsOneOrNull()
}
}
在查询的最后,我们添加executeAsOneOrNull() ,因为如果在数据库中找不到笔记,我们的查询可能会返回一个空。
删除一个音符
override suspend fun deleteNoteById(id: Long) {
return withContext(Dispatchers.IO) {
queries.deletePersonById(id)
}
}
在这里,我们传递我们希望删除的笔记的id 。
第6步 - 定义一个视图模型
在这一步,我们定义一个ViewModel ,它将与我们刚刚创建的数据源交互。
@HiltViewModel
class NotesViewModel @Inject constructor(private val noteDataSource: NoteDataSource): ViewModel() {
val notes = noteDataSource.getAllNotes()
var noteDetails = MutableLiveData<NotesEntity>()
fun insertNote(title: String, description: String){
if (title.isNullOrEmpty() || description.isNullOrEmpty()){
return
}
viewModelScope.launch {
noteDataSource.insertNote(title, description)
}
}
fun deleteNote(id: Long){
viewModelScope.launch {
noteDataSource.deleteNoteById(id)
}
}
fun getNoteById(id: Long){
viewModelScope.launch {
noteDetails.value = noteDataSource.getNoteById(id)
}
}
}
第7步 - 设置匕首柄
在这里,我们将设置dagger hilt,以便我们可以注入我们的数据源和数据库。
首先,在你的root 包中,创建一个名为NotesApp 的类,并添加hilt ,如下所示。
@HiltAndroidApp
class NotesApp : Application()
不要忘记在你的清单文件中指定该类为名称。
定义另一个包,并将其命名为di 。右键单击该包并创建一个新的object 文件,名为AppModule 。
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
...
}
然后我们提供以下依赖性。
- 数据库驱动程序
SQLDelight需要AndroidSqliteDriver ,以便它能够创建和使用数据库。在AndroidSqliteDriver ,我们传递模式(已生成)、上下文和我们的数据库名称,如下所示。
@Provides
@Singleton
fun provideSqlDriver(app: Application): SqlDriver {
return AndroidSqliteDriver(
schema = NotesDatabase.Schema,
context = app,
name = "notes.db"
)
}
- 数据源
使用上面的驱动,我们可以构建数据源,如下所示。
@Provides
@Singleton
fun providesNotesDataSource(driver: SqlDriver): NoteDataSource {
return NoteDataSourceImpl(
NotesDatabase(driver)
)
}
第8步 - 在活动上下功夫
最后,我们将定义我们的用户活动,一个用于添加笔记,另一个用于显示笔记的列表。
添加笔记
你可以创建一个类似的布局,它有两个EditText :一个是笔记标题,另一个是笔记描述和保存笔记的按钮。

在相应的活动中,添加以下代码,当保存按钮被点击时,保存一个笔记。
binding.buttonSave.setOnClickListener {
val title = binding.edtTitle.text.toString()
val description = binding.edtDescription.text.toString()
if (title.isNullOrEmpty() || description.isNullOrEmpty()){
return@setOnClickListener
}
CoroutineScope(Dispatchers.Main).launch {
viewModel.insertNote(title, description)
startActivity(Intent(this@AddNoteActivity, MainActivity::class.java))
finish()
}
}
显示笔记
创建一个类似的布局,它有一个Recyclerview ,用于显示笔记的列表。

不要忘记为
Recyclerview,创建一个相应的行布局。
另外,确保你已经定义了一个适配器,你的
Recyclerview将会使用。适配器将使用的模型类将是由SQLDelight生成的NotesEntity。
在相应的活动中,添加以下代码来显示注释。
CoroutineScope(Dispatchers.Main).launch {
viewModel.notes.collect { notes ->
adapter.submitList(notes)
binding.notesRecycler.adapter = adapter
}
}
删除一个注释
如果你已经为你的Recyclerview项目设置了onClickListeners ,你可以从ViewModel ,这样调用删除方法。
viewModel.deleteNote(note.id)
更新一个注解
如果你想从ViewModel中的函数getNoteById(id: Long) ,更新一个笔记,你要传入它的id。
演示

结论
在本教程中,我们已经讨论了什么是SQL Delight数据库,它与Room数据库的比较,以及它的优点和缺点。
然后,我们通过创建一个简单的笔记应用程序在Android中实现了SQL Delight数据库。