将SQL Delight与Android中的Room数据库进行比较

867 阅读7分钟

安卓系统中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项目,如下图所示。

project

第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 " 设置 " 插件,然后点击市场,搜索该插件。

plugin

第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

new_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 {
    ...
}

然后我们提供以下依赖性。

  1. 数据库驱动程序

SQLDelight需要AndroidSqliteDriver ,以便它能够创建和使用数据库。在AndroidSqliteDriver ,我们传递模式(已生成)、上下文和我们的数据库名称,如下所示。

@Provides
@Singleton
fun provideSqlDriver(app: Application): SqlDriver {
    return AndroidSqliteDriver(
        schema = NotesDatabase.Schema,
        context = app,
        name = "notes.db"
    )
}
  1. 数据源

使用上面的驱动,我们可以构建数据源,如下所示。

@Provides
@Singleton
fun providesNotesDataSource(driver: SqlDriver): NoteDataSource {
    return NoteDataSourceImpl(
        NotesDatabase(driver)
    )
}

第8步 - 在活动上下功夫

最后,我们将定义我们的用户活动,一个用于添加笔记,另一个用于显示笔记的列表。

添加笔记

你可以创建一个类似的布局,它有两个EditText :一个是笔记标题,另一个是笔记描述和保存笔记的按钮

add_note

在相应的活动中,添加以下代码,当保存按钮被点击时,保存一个笔记。

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 ,用于显示笔记的列表。

notes_list

不要忘记为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。

演示

demo

结论

在本教程中,我们已经讨论了什么是SQL Delight数据库,它与Room数据库的比较,以及它的优点和缺点。

然后,我们通过创建一个简单的笔记应用程序在Android中实现了SQL Delight数据库。