Android Kotlin-使用一对多实体关系时发生错误

时间:2020-05-25 00:05:01

标签: android database sqlite kotlin android-room

最初创建应用程序时,它使用cardName并将其托管在RecyclerView中,该Viewer按名称保存了每个卡的列表。由于我还不熟悉开发,所以这样做是为了看看是否可以让RecyclerView仅使用名称。现在,我希望能够以一对多的关系向每个卡添加类别,并且按照https://developer.android.com/training/data-storage/room/relationships下的说明进行操作,但是由于我遇到以下错误,因此我可能遗漏了某些内容。理想情况下,该应用程序应与以前一样工作,仅显示名称,但现在还具有其他值,随着时间的推移,我可以将这些值慢慢添加到片段中。我非常感谢您的协助,因为我可能忽略了某些事项。预先感谢。

package com.example.android.pointmax.database

import androidx.room.*

@Entity
data class Card(
    @PrimaryKey(autoGenerate = true)
    var cardId: Long = 0L,
    var cardName: String
)

@Entity
data class Category(
    @PrimaryKey(autoGenerate = true)
    val categoryId: Long = 0L,
    val cardCategoryId: Long = 0L,
    var type: String = "General",
    var earnRate: Double = 1.0,
    var protection: Int = 0,
    var redeemValue: String = "Cash"
)

data class CreditCards(
    @Embedded val card: Card,
    @Relation(
        parentColumn = "cardId",
        entityColumn = "cardCategoryId"
    )
    val categories: List<Category>
)

CardDao

package com.example.android.pointmax.database

import androidx.lifecycle.LiveData
import androidx.room.*


@Dao
interface CardDao {
    @Transaction
    @Query("SELECT * from Card")
    fun getCards(): LiveData<List<CreditCards>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(card: Card)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertCategory(category: Category)

    @Query("DELETE FROM Card")
    suspend fun deleteAll()

    @Query("DELETE FROM Card WHERE cardName = :name")
    suspend fun deleteByName(name: String)

    @Query("UPDATE Card SET cardName = :newName WHERE cardName = :oldName")
    suspend fun editName(newName: String, oldName: String)
}

CardRepository

package com.example.android.pointmax.database

import androidx.lifecycle.LiveData

// Declares the DAO as a private property in the constructor. Pass in the DAO
// instead of the whole database, because you only need access to the DAO
class CardRepository(private val cardDao: CardDao) {

    // Room executes all queries on a separate thread.
    // Observed LiveData will notify the observer when the data has changed.
    val allCards: LiveData<List<CreditCards>> = cardDao.getCards()

    suspend fun insert(card: Card) {
        cardDao.insert(card)
    }

    suspend fun insertCategory(category: Category) {
        cardDao.insertCategory(category)
    }

    suspend fun deleteByName(name: String){
        cardDao.deleteByName(name)
    }

    suspend fun editName(newName: String, oldName: String){
        cardDao.editName(newName, oldName)
    }
}

CardRoomDatabase

package com.example.android.pointmax.database

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch


// Annotates class to be a Room Database with a table (entity) of the Card class
@Database(entities = arrayOf(Card::class), version = 1, exportSchema = false)
public abstract class CardRoomDatabase : RoomDatabase() {

    abstract fun cardDao(): CardDao

    companion object {
        // Singleton prevents multiple instances of database opening at the
        // same time.
        @Volatile
        private var INSTANCE: CardRoomDatabase? = null

        fun getDatabase(
            context: Context,
            scope: CoroutineScope
        ): CardRoomDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    CardRoomDatabase::class.java,
                    "card_database"
                ).addCallback(CardDatabaseCallback(scope)).build()
                INSTANCE = instance
                return instance
            }
        }

        private class CardDatabaseCallback(
            private val scope: CoroutineScope
        ) : RoomDatabase.Callback() {

            override fun onOpen(db: SupportSQLiteDatabase) {
                super.onOpen(db)
                INSTANCE?.let { database ->
                    scope.launch {
                        populateDatabase(database.cardDao())
                    }
                }
            }

            suspend fun populateDatabase(cardDao: CardDao) {
                // Delete all content here.
                cardDao.deleteAll()

                // Add sample cards.
                var card = Card(cardName = "Petal Credit Card")
                cardDao.insert(card)
                var category = Category(cardCategoryId = card.cardId, type = "General", earnRate = 1.5, protection = 0, redeemValue = "cash")
                cardDao.insertCategory(category)
//                card = Card(cardName = "Discover IT")
//                cardDao.insert(card)
            }
        }
    }
}

构建

执行任务:项目C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax中的[:app:assembleDebug]

任务:app:preBuild截止日期 任务:app:preDebugBuild UP-TO-DATE 任务:app:compileDebugAidl NO-SOURCE 任务:app:generateDebugBuildConfig更新日期 任务:app:writeDebugApplicationId更新日期 任务:app:compileDebugRenderscript NO-SOURCE 任务:app:generateSafeArgsDebug截止日期 任务:app:prepareLintJar截止日期 任务:app:prepareLintJarForPublish截止日期 任务:app:generateDebugSources更新日期 任务:app:dataBindingExportBuildInfoDebug截止日期 任务:app:dataBindingMergeDependencyArtifactsDebug截止日期 任务:app:dataBindingMergeGenClassesDebug截止日期 任务:app:generateDebugResValues截止日期 任务:app:generateDebugResources最新 任务:app:mergeDebugResources最新 任务:app:dataBindingGenBaseClassesDebug截止日期 任务:app:dataBindingExportFeaturePackageIdsDebug截止日期 任务:app:mainApkListPersistenceDebug截止日期 任务:app:createDebugCompatibleScreenManifests截止日期 任务:app:extractDeepLinksDebug截止日期 任务:app:processDebugManifest截止日期 任务:app:processDebugResources截止日期 任务:app:kaptGenerateStubsDebugKotlin 任务:app:mergeDebugShaders截止日期 任务:app:compileDebugShaders截止日期 任务:app:generateDebugAssets更新日期 任务:app:mergeDebugAssets截止日期 任务:app:processDebugJavaRes NO-SOURCE 任务:app:checkDebugDuplicateClasses最新 任务:app:desugarDebugFileDependencies截止日期 任务:app:mergeExtDexDebug截止日期 任务:app:mergeDebugJniLibFolders截止日期 任务:app:mergeDebugNativeLibs截止日期 任务:app:stripDebugDebugSymbols截止日期 任务:app:validateSigningDebug截止日期

任务:app:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与当前运行时版本4.7.1不匹配用于分析器编译的ANTLR运行时版本4.5.3与当前运行时版本4.7.1不匹配用于代码生成的ANTLR工具版本4.5.3与匹配用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3与当前运行时版本4.7.1C不匹配:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CreditCards.java:12:错误:查询存在问题:[SQLITE_ERROR] SQL错误或缺少数据库(没有这样的表:Category) 私有最终java.util.List类别= null; ^ C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CardDao.java:17:错误:参数类型必须是一个类用@Entity或其集合/数组注释。 com.example.android.pointmax.database.CreditCards卡,@ org.jetbrains.annotations.NotNull()

任务:app:kaptDebugKotlin失败 ^ 失败:构建失败,并出现异常。

*出了什么问题: 任务':app:kaptDebugKotlin'的执行失败。

执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障 java.lang.reflect.InvocationTargetException(无错误消息)

  • 尝试: 使用--stacktrace选项运行以获取堆栈跟踪。使用--info或--debug选项运行,以获取更多日志输出。与--scan一起运行以获得完整的见解。
  • https://help.gradle.org上获得更多帮助

11秒内失败 29个可执行的任务:2个已执行,27个是最新的

编辑: BindingAdapter

package com.example.android.pointmax

import androidx.databinding.BindingAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CreditCards


/**
 * When there are no Cards (data is null), hide the [RecyclerView], otherwise show it.
 */
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView, data: List<CreditCards>?) {
    val adapter = recyclerView.adapter as CardAdapter
    adapter.submitList(data)
}

CardAdapter

package com.example.android.pointmax

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CreditCards
import com.example.android.pointmax.databinding.RecyclerviewItemBinding
import com.example.android.pointmax.ui.wallet.WalletViewModel
import kotlinx.android.synthetic.main.recyclerview_item.view.*


class CardAdapter(val onClickListener: OnClickListener) :
    ListAdapter<CreditCards, CardAdapter.CardViewHolder>(DiffCallback) {

    /**
     * The CardViewHolder constructor takes the binding variable from the associated
     * LayoutViewItem, which nicely gives it access to the full [Card] information.
     */
    class CardViewHolder (private var binding: RecyclerviewItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind (cards: CreditCards){
            binding.creditCard = cards
            // This is important, because it forces the data binding to execute immediately,
            // which allows the RecyclerView to make the correct view size measurements
            binding.executePendingBindings()
        }
    }

    /**
     * Allows the RecyclerView to determine which items have changed when the [List] of [Card]
     * has been updated.
     */
    companion object DiffCallback : DiffUtil.ItemCallback<CreditCards>() {
        override fun areItemsTheSame(oldItem: CreditCards, newItem: CreditCards): Boolean {
            return oldItem.card === newItem.card
        }

        override fun areContentsTheSame(oldItem: CreditCards, newItem: CreditCards): Boolean {
            return oldItem.card.cardName == newItem.card.cardName
        }
    }

    /**
     * Create new [RecyclerView] item views (invoked by the layout manager)
     */
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
        return CardViewHolder(RecyclerviewItemBinding.inflate(LayoutInflater.from(parent.context)))
    }

    /**
     * Replaces the contents of a view (invoked by the layout manager)
     */
    override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
        val current = getItem(position)
        holder.itemView.setOnClickListener {
            onClickListener.onClick(current)
        }
        holder.bind(current)
    }


    /**
     * Custom listener that handles clicks on [RecyclerView] items.  Passes the [CreditCards]
     * associated with the current item to the [onClick] function.
     * @param clickListener lambda that will be called with the current [Card]
     */
    class OnClickListener(val clickListener: (cards : CreditCards) -> Unit) {
        fun onClick(cards:CreditCards) = clickListener(cards)
    }
}

Recyclerview_item

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>
        <variable
            name="creditCard"
            type="com.example.android.pointmax.database.CreditCards" />
    </data>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <androidx.constraintlayout.widget.ConstraintLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <TextView
                android:id="@+id/card_name"
                style="@style/card_title"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@{creditCard.card.cardName.toString()}"
                android:background="@android:color/holo_orange_light"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                android:layout_marginBottom="@dimen/activity_vertical_margin"
                />
        </androidx.constraintlayout.widget.ConstraintLayout>
    </LinearLayout>
</layout>

fragment_wallet.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <data>
        <variable
            name="viewModel"
            type="com.example.android.pointmax.ui.wallet.WalletViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.example.android.pointmax.MainActivity">

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/wallet_recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:listitem="@layout/recyclerview_item"
                app:layout_constraintBottom_toTopOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="parent"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:listData="@{viewModel.allCards}"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

WalletViewModel

package com.example.android.pointmax.ui.wallet

import android.app.Application
import androidx.lifecycle.*
import com.example.android.pointmax.database.Card
import com.example.android.pointmax.database.CardRepository
import com.example.android.pointmax.database.CardRoomDatabase
import com.example.android.pointmax.database.CreditCards
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job

class WalletViewModel(application: Application) : AndroidViewModel(application) {
    private val repository: CardRepository
    // Using LiveData and caching what getAlphabetizedWords returns has several benefits:
    // - We can put an observer on the data (instead of polling for changes) and only update the
    //   the UI when the data actually changes.
    // - Repository is completely separated from the UI through the ViewModel.

    // The external LiveData interface to the property is immutable, so only this class can modify
    val allCards: LiveData<List<CreditCards>>

    // Internally, we use a MutableLiveData to handle navigation to the selected cxa
    private val _navigateToSelectedCard = MutableLiveData<Card>()

    // The external immutable LiveData for the navigation property
    val navigateToSelectedCard: LiveData<Card>
        get() = _navigateToSelectedCard

    // Create a Coroutine scope using a job to be able to cancel when needed
    private var viewModelJob = Job()

    // the Coroutine runs using the Main (UI) dispatcher
    private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)

    /**
     * When the card is clicked, set the [_navigateToSelectedCard] [MutableLiveData]
     * @param card The [Card] that was clicked on.
     */
    fun displayCardDetails(card: Card) {
        _navigateToSelectedCard.value = card
    }

    /**
     * After the navigation has taken place, make sure navigateToSelectedProperty is set to null
     */
    fun displayCardDetailsComplete() {
        _navigateToSelectedCard.value = null
    }

    init {
        val cardsDao = CardRoomDatabase.getDatabase(application, viewModelScope).cardDao()
        repository = CardRepository(cardsDao)
        allCards = repository.allCards
    }
}

关于应用中的一对多关系,我仍然面临错误:

任务:app:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与当前运行时版本4.7.1不匹配用于分析器编译的ANTLR运行时版本4.5.3与当前运行时版本4.7.1不匹配用于代码生成的ANTLR工具版本4.5.3与匹配用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3与当前运行时版本4.7.1C不匹配:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CreditCards.java:12:错误:查询存在问题:[SQLITE_ERROR] SQL错误或缺少数据库(没有这样的表:Category) 私有最终java.util.List类别= null; ^ C:\ Users \ jjrog \ AndroidStudioProjects \ PointMax \ app \ build \ tmp \ kapt3 \ stubs \ debug \ com \ example \ android \ pointmax \ database \ CardDao.java:22:错误:com.example.android.pointmax。 database.CardDao是com.example.android.pointmax.database.CardRoomDatabase的一部分,但该实体不在数据库中。也许您忘了将com.example.android.pointmax.database.Category添加到@Database的实体部分? 公共抽象java.lang.Object insertCategory(@ org.jetbrains.annotations.NotNull() [WARN]请求了增量注释处理,但是由于以下处理器不是增量处理器,因此不支持该功能:androidx.room.RoomProcessor(DYNAMIC)。

任务:app:kaptDebugKotlin失败 ^ 失败:构建失败,并出现异常。

  • 出了什么问题: 任务':app:kaptDebugKotlin'的执行失败。

    执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障 java.lang.reflect.InvocationTargetException(无错误消息)

堆栈跟踪:

由以下原因引起:org.gradle.workers.internal.DefaultWorkerExecutor $ WorkExecutionException:执行org.jetbrains.kotlin.gradle.internal.KaptExecution时发生故障

由以下原因引起:java.lang.reflect.InvocationTargetException

起因:org.jetbrains.kotlin.kapt3.base.util.KaptBaseError:注释处理时出错

1 个答案:

答案 0 :(得分:1)

我认为,这里的问题出在您的DAO中:

    formset = DetailMatiereDetailServiceFormSet(form_kwargs={'annee': annee})
    NameError: name 'annee' is not defined

信用卡-只是数据类,不是Room的实体。没有与之关联的SQLite表。因此,您不能使用它来插入。假设这个类-对于Room是“只读的”(在查询过程中它仅在运行时填充了数据),并且只能将其用作查询中结果的类型。

您还有另外两个实体- Card Category ,它们每个都与SQLite表关联。您应该为它们实现@Insert方法。这些表(在其中一个或两个表中)发生更改后,您将通过查询获得新的结果,从而得到 CreditCards 值。