Kotlin中的Room Persistence库实现

18

我正在使用 Kotlin 实现 Room 持久性库来实现我的数据库。

以下是我的 EntityDaoDatabase 类:

Food.kt

@Entity
class Food(@ColumnInfo(name = "food_name") var foodName: String,
           @ColumnInfo(name = "food_desc") var foodDesc: String,
           @ColumnInfo(name = "protein") var protein: Double,
           @ColumnInfo(name = "carbs") var carbs: Double,
           @ColumnInfo(name = "fat") var fat: Double)
{
    @ColumnInfo(name = "id")
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
    @ColumnInfo(name = "calories")
    var calories: Double = 0.toDouble()
}

个性化食品.kt

@Entity(primaryKeys = arrayOf("food_id","date"))
class PersonalizedFood(@ColumnInfo(name = "quantity") var quantity: Int,
                       @ColumnInfo(name = "unit") var unit: String,
                       @ColumnInfo(name = "date") var date: Date){

    @ColumnInfo(name = "food_id")
    var foodId:Long = 0
}

FoodDao.kt

@Dao
interface FoodDao {

    companion object{
        const val ID = "id"
        const val NAME = "name"
        const val PROTEIN = "protein"
        const val DESC = "desc"
        const val CARBS = "carbs"
        const val FAT = "fat"

        const val DATE = "date"
        const val FOOD_ID = "food_id"

        const val ALL_FOOD_LIST = "food"
        const val PERSONALISED_FOOD_LIST = "personalised_food"
    }

    /**
     * Returns food details of a food given by food_id
     */
    @Query("SELECT * FROM $ALL_FOOD_LIST WHERE $ID=:food_id")
    fun getFoodDetails(food_id:Long):Food

    /**
     * Inserts food items in all_food_list
     */
    @Insert
    fun addFoodList(list:ArrayList<Food>)

    @Insert(onConflict = REPLACE)
    fun saveFood(food:PersonalizedFood)

    @Query("SELECT * FROM $PERSONALISED_FOOD_LIST WHERE $FOOD_ID=:foodId and $DATE=:date")
    fun getFood(foodId:Int, data:Date):PersonalizedFood

    @Query("SELECT * FROM $ALL_FOOD_LIST where $ID in (select $FOOD_ID from $PERSONALISED_FOOD_LIST where $DATE = :date)")
    fun getFood(date:Date):ArrayList<Food>
}

Converter.kt

class Converter {

    companion object{
        @TypeConverter
        fun fromTimestamp(value: Long?): Date? {
            return if (value == null) null else Date(value)
        }

        @TypeConverter
        fun dateToTimestamp(date: Date): Long {
            return date.time
        }
    }
}

食品数据库.kt

@Database(entities = arrayOf(Food::class, PersonalizedFood::class), version = 1)
@TypeConverters(Converter::class)
abstract class FoodDatabase : RoomDatabase(){
    abstract fun foodDao():FoodDao

    companion object{
        private val databaseName = "diet"

        var dbInstance:FoodDao? = null
        fun getInstance(context:Context):FoodDao?{
            if(dbInstance == null)
                dbInstance = Room.inMemoryDatabaseBuilder(context, FoodDatabase::class.java).build().foodDao()
            return dbInstance;
        }
    }
}

当我运行以下代码以创建数据库时:

FoodDatabase.getInstance(baseContext)?.getFood(Calendar.getInstance().time)

它给我以下异常:

  

Caused by: java.lang.RuntimeException: cannot find implementation for com.chandilsachin.diettracker.database.FoodDatabase. FoodDatabase_Impl does not exist      at android.arch.persistence.room.Room.getGeneratedImplementation(Room.java:90)      at android.arch.persistence.room.RoomDatabase$Builder.build(RoomDatabase.java:340)      at com.chandilsachin.diettracker.database.FoodDatabase$Companion.getInstance(FoodDatabase.kt:21)      at com.chandilsachin.diettracker.MainActivity$SetUpFoodDatabase.doInBackground(MainActivity.kt:95)      at com.chandilsachin.diettracker.MainActivity$SetUpFoodDatabase.doInBackground(MainActivity.kt:77)      at android.os.AsyncTask$2.call(AsyncTask.java:295)      at java.util.concurrent.FutureTask.run(FutureTask.java:237)      at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:234)       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)       at java.lang.Thread.run(Thread.java:818)

有人在Kotlin中实现了Room Persistence吗?

编辑:此问题被标记为此问题的重复项。虽然问题陈述相同,但提供的解决方法无法解决我的问题。解决方案表示我必须添加将annotationProcessor替换为kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"依赖项。我进行了这些更改,结果在项目构建时导致gradle错误。

  

Information: Gradle tasks[:app:assembleDebug] Warning:warning:     来自注释处理器“android.arch.persistence.room.RoomProcessor”的支持源版本“RELEASE_7”小于-source'1.8'     Warning:warning:以下选项未被任何处理器识别:“[kapt.kotlin.generated]”     /Users/BBI-M1025/Documents/BBI/Workspace_fun/Android/diet-tracker/app/src/main/java/com/chandilsachin/diettracker/database/Food.kt      Error:(1, 1)处理注释时发生了一些错误。请参见上面的错误消息。      Error:任务执行失败':app:kaptDebugKotlin'

     
    

编译错误。有关更多详细信息,请参见日志             信息:10秒内构建失败             信息:2个错误             信息:2个警告             信息:请参阅完整的输出

  

我也附上了我的gradle文件:

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.chandilsachin.diettracker"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    compile 'com.android.support:appcompat-v7:25.0.1'
    compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8'
    compile 'com.android.support:cardview-v7:25.0.1'
    compile 'com.android.support:recyclerview-v7:25.0.1'
    compile 'com.github.ne1c:rainbowmvp:1.2.1'
    compile "org.jetbrains.anko:anko-commons:0.10.0"

    /*annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"
    compile "android.arch.lifecycle:extensions:1.0.0-alpha1"
    annotationProcessor "android.arch.lifecycle:compiler:1.0.0-alpha1"*/

    compile "android.arch.persistence.room:runtime:1.0.0-alpha1"
    annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"
    kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"

    testCompile 'junit:junit:4.12'
}
repositories {
    mavenCentral()
}

有人遇到过这个问题吗?


你可以在原始帖子上进行评论。也可以添加赏金。但我关闭这个帖子是因为我感觉它是一个重复的问题。 - Raghunandan
@Raghunandan,我已在原始帖子中发表评论。但要为此问题添加赏金,您必须打开此问题,因为这里没有赏金链接。 - Sachin Chandil
在另一个问题上添加悬赏。或者在帖子下发表评论。即使我没有在之前关闭它,这个帖子最终也会被某人关闭,因为它是重复的。 - Raghunandan
你不能打开这个问题吗? - Sachin Chandil
我可以,但问题是这是一个重复的问题,因此我关闭了它。如果您仍然认为这是错误的,请转到https://meta.stackoverflow.com/并指出这个问题,有人应该回答您的查询。 - Raghunandan
显示剩余2条评论
5个回答

24

在苦思冥想了一段时间后,我终于找到了解决方案。

这真的很难,因为现在还没有官方教程、博客等来帮助解决这个问题。

我必须尝试多种组合的gradle插件和依赖项,因为我知道gradle配置有问题。

让我们来看看解决方案:

我不得不从build.gradle(:module)文件中删除apply plugin: 'kotlin-kapt',并将annotationProcessor "android.arch.persistence.room:compiler:1.0.0-alpha1"替换为kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"

这是编译代码成功的gradle配置。

但是还有更多需要检查的地方。您必须初始化您的@Entity class属性,与Room Persistence lib文档中给出的Java不同。虽然有getter setter,但未说明要创建带初始化的构造函数。 所以我不得不改变我的@Entity类如下:

@Entity(tableName = "all_food_list")
class Food (@ColumnInfo(name = "food_name") var foodName: String = "",
            @ColumnInfo(name = "food_desc") var foodDesc: String = "",
            @ColumnInfo(name = "protein") var protein: Double = 0.0,
            @ColumnInfo(name = "carbs") var carbs: Double = 0.0,
            @ColumnInfo(name = "fat") var fat: Double = 0.0,
            @ColumnInfo(name = "calories") var calories: Double = 0.0)
{
    @ColumnInfo(name = "id")
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0
}

现在讨论类型转换。与Java不同的是,你需要创建普通函数而非静态函数(伴生对象):

class Converters{

        @TypeConverter
        fun fromTimestamp(value: String): Calendar {
            val arr = value.split("-")
            val cal = Calendar.getInstance()
            cal.set(arr[0].toInt(), arr[1].toInt(), arr[2].toInt())
            return cal
        }

        @TypeConverter
        fun dateToTimestamp(date: Calendar): String {
            return "${date.get(Calendar.DATE)}-${date.get(Calendar.MONTH)+1}-${date.get(Calendar.YEAR)}"
        }

}

我将添加build.gradle文件以使其更加清晰:

build.gradle(:project)

buildscript {
    ext.kotlin_version = '1.1.2-4'
    ext.gradle_version_stable = '2.3.2'
    ext.gradle_version_preview = '3.0.0-alpha1'
    ext.anko_version = '0.10.0'
    repositories {
        maven { url 'https://maven.google.com' }
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        maven { url 'https://maven.google.com' }
        jcenter()
        maven { url "https://jitpack.io" }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

build.gradle(:module)

翻译为:构建.gradle(:模块)。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
    defaultConfig {
        applicationId "com.chandilsachin.diettracker"
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}


dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    ...
    ...
    // room persistence dependency 
    compile "android.arch.persistence.room:runtime:1.0.0-alpha1"
    kapt "android.arch.persistence.room:compiler:1.0.0-alpha1"

    testCompile 'junit:junit:4.12'
}
repositories {
    mavenCentral()
}

我认为这就是我做的一切,使我的代码能够正常运行。

希望这也对其他人有所帮助。


我正在使用AS 2,我认为Room还不支持Java 8,因此当您尝试与kotlin一起使用时会失败(我猜它需要J8?)至少错误看起来是这样的。 - RichyHBM
我没有使用数据绑定。 - Sachin Chandil
删除 apply plugin: 'kotlin-kapt' 对我来说起了作用。谢谢。 - Yasitha Waduge
1
这对我来说不起作用,因为我正在使用dagger2,而没有使用kapt它无法编译:( 我得到的错误与你们不同,正如我在这个问题中提到的: https://dev59.com/Dqfja4cB1Zd3GeqPt1Kf - Sandip Fichadiya
1
删除 apply plugin: 'kotlin-kapt' 应该是不必要的,如果我删除它,其他库如 Glide 将会出现问题。 - Manohar
显示剩余5条评论

2
这是我的Gradle文件,我不需要添加这些插件。
build.gradle(项目):
buildscript {
    ext.kotlin_version = '1.1.2-4'
    ext.lifecycle_version = '1.0.0-alpha1'
    ext.room_version = '1.0.0-alpha1'

    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

    }
}

build.gradle (app)

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'

//enable anotation processing with kotlin, disabled by default
kapt {
    generateStubs = true
}


android {
/**
...
**/
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    //kotlin
    compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"

    //support
    compile 'com.android.support:appcompat-v7:25.3.1'

    //google architecture
    compile "android.arch.lifecycle:runtime:$lifecycle_version"
    compile "android.arch.lifecycle:extensions:$lifecycle_version"
    annotationProcessor "android.arch.lifecycle:compiler:$lifecycle_version"
    kapt "android.arch.lifecycle:compiler:$lifecycle_version"

    //database
    compile "android.arch.persistence.room:runtime:$room_version"
    annotationProcessor "android.arch.persistence.room:compiler:$room_version"
    kapt "android.arch.persistence.room:compiler:$room_version"

}
repositories {
    mavenCentral()
}

然后运行菜单构建->生成项目以创建实现类。 希望这有所帮助。

是的,希望我解决了这个问题。除了插件之外,还有一些代码更改。 - Sachin Chandil

0

当我试图从Java迁移到Kotlin时,遇到了相同的问题:

RuntimeException: cannot find implementation for AppDatabase. AppDatabase_Impl does not exist

我尝试了这个问题中的所有答案,但都没有起作用,然后我找到了一篇文章:Kotlinlang Tutorials - Android Frameworks Using Annotation Processing

在 Kotlin 中,您可以使用 Kotlin 注解处理工具(kapt)而不是 annotationProcessor 来类似于 Java 的方式指定依赖项。

然后我通过将所有的 annotationProcessor 改为 kapt 修改了 build.gradle(Module:app),它就起作用了:

--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,6 +2,8 @@ apply plugin: 'com.android.application'

 apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-kapt'
 apply plugin: 'kotlin-android-extensions'

 android {
     // Butter Knife
     implementation "com.jakewharton:butterknife:$butterknife_version"
-    annotationProcessor "com.jakewharton:butterknife-compiler:$butterknife_version"
+    kapt "com.jakewharton:butterknife-compiler:$butterknife_version"

     // Room
     implementation "android.arch.persistence.room:runtime:$arch_lifecycle_version"
-    annotationProcessor "android.arch.persistence.room:compiler:$arch_lifecycle_version"
+    kapt "android.arch.persistence.room:compiler:$arch_lifecycle_version"
     // LifeCycle
     implementation "android.arch.lifecycle:runtime:$arch_lifecycle_version"
     implementation "android.arch.lifecycle:extensions:$arch_lifecycle_version"
     implementation "android.arch.lifecycle:common-java8:$arch_lifecycle_version"
-    annotationProcessor "android.arch.lifecycle:compiler:$arch_lifecycle_version"
+    kapt "android.arch.lifecycle:compiler:$arch_lifecycle_version"

     // Dagger
     implementation "com.google.dagger:dagger:$dagger_version"
     implementation "com.google.dagger:dagger-android-support:$dagger_version"
-    annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
-    annotationProcessor "com.google.dagger:dagger-android-processor:$dagger_version"
+    kapt "com.google.dagger:dagger-compiler:$dagger_version"
+    kapt "com.google.dagger:dagger-android-processor:$dagger_version"
}

如果您使用Butter Knife,那么您也应该改用kapt,否则注入的视图可能为空。

0

我用Java制作了一个类似的示例,也遇到了同样的问题,解决方法是添加APT行apt "android.arch.persistence.room:compiler:1.0.0-alpha1"

不要使用:apply plugin: 'kotlin-kapt'。删除这一行!!

在重新运行之前,您必须清除项目并重新构建它,并尝试卸载手机或虚拟设备上的现有应用程序。 希望能帮到您。


1
Java的注解处理器是apt,而Kotlin的则是kapt。因此,如果您正在为Java编写代码,必须使用apt。我认为您的答案在这里不适用。 - Sachin Chandil
我对你的代码进行了观察,你必须删除KAPT中的那一行。我知道我的代码是APT,因为它是JAVA。只是想告诉你我曾经遇到过同样的问题,以及我是如何解决的。所以......这是针对你的问题,而不是我的代码。 - Martín De Girolamo

0
在Dao接口中,请检查注释(如@Query..)是否从room数据库类导入,而不是从其他地方导入。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接