Android Kotlin-使用一对多实体关系时出错
当我最初构建我的应用程序时,它采用了cardName并将其托管在一个RecyclerView中,该视图按名称保存了每张卡的列表。因为我还是开发新手,所以我这样做是为了看看我是否可以让RecyclerView只处理名称。现在,我希望能够在一对多的关系中为每张卡添加类别,我按照下面的说明进行了操作,但我可能遗漏了一些内容,因为我遇到了下面的错误。理想情况下,应用程序应该和以前一样工作,只显示名称,但现在也有额外的值,随着时间的推移,我可以慢慢添加到片段中。我非常感谢任何帮助,因为我可能忽略了一些事情。提前谢谢 卡片 建造 正在执行项目C:\Users\JJLog\AndroidStudio项目\PointMax中的任务:[:app:assembleDebug] 任务:应用程序:预构建最新 任务:app:preDebugBuild-update 任务:应用程序:CompiledBugaidl无源代码 任务:app:generateDebugBuildConfig最新 任务:应用程序:WritedBugApplicationID最新 任务:应用程序:CompiledBugRenderScript无源代码 任务:应用程序:生成最新的FeargsDebug 任务:app:prepareLintJar最新 任务:应用程序:准备发布最新版本 任务:应用程序:生成最新的BugSources 任务:应用程序:dataBindingExportBuildInfoDebug最新 任务:应用程序:数据绑定合并依赖项工件缺陷最新 任务:应用程序:DataBindingMergenclasseDbug最新 任务:应用程序:生成最新的BugResValues 任务:应用程序:生成最新的BugResources 任务:应用程序:合并调试资源最新 任务:应用程序:DataBindingGenBaseClasseDebug最新 任务:应用程序:DataBindingExportFeaturePackageIDSDDebug最新 任务:应用程序:mainApkListPersistenceDebug最新 任务:应用程序:CreateDebuggCompatibleScreenManifests最新 任务:应用程序:extractDeepLinksDebug最新 任务:应用程序:processDebugManifest最新 任务:应用程序:processDebugResources最新 任务:应用程序:kaptGenerateStubsDebugKotlin 任务:应用程序:合并调试着色器最新 任务:应用程序:最新的CompiledBugShader 任务:应用程序:生成最新的BugAssets 任务:应用程序:合并调试资产最新 任务:app:processDebugJavaRes NO-SOURCE 任务:应用程序:CheckDebugDuplicateClass最新 任务:应用程序:DesugardBugFileDependencies最新 任务:应用程序:mergeExtDexDebug最新 任务:app:mergeDebugJniLibFolders最新 任务:应用程序:mergeDebugNativeLibs最新 任务:应用程序:条带调试符号最新 任务:应用程序:验证设计调试最新 任务:应用程序:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3不匹配用于代码生成的当前运行时版本4.7.1ANTLR工具版本4.5.3与用于解析器的当前运行时版本4.7.1ANTLR运行时版本4.5.3不匹配编译与当前运行时版本4.7.1C不匹配:\Users\jjlog\AndroidStudioProjects\PointMax\app\build\tmp\kapt3\stubs\debug\com\example\android\PointMax\database\CreditCards.java:12:错误:查询有问题:[SQLITE\u error]SQL错误或缺少数据库没有这样的表:Category private final java.util.List categories=null; ^C:\Users\jjlog\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 任务:应用程序:kaptDebugKotlin失败 ^ 失败:生成失败,出现异常 *出了什么问题: 任务“:app:kaptDebugKotlin”的执行失败 执行org.jetbrains.kotlin.gradle.internal.KaptExecution时出错 java.lang.reflect.InvocationTargetException无错误消息 尝试: 使用-stacktrace选项运行以获取堆栈跟踪。使用-info或-debug选项运行以获得更多日志输出。使用-scan运行以获得完整的洞察力。 得到更多的帮助 生成在11秒内失败 29项可执行任务:2项已执行,27项最新 编辑: 绑定适配器 卡适配器 回收视图项目 fragment_wallet.xml WalletViewModel 关于应用程序中的一对多关系,我仍然面临错误: 任务:应用程序:kaptDebugKotlin 用于代码生成的ANTLR工具版本4.5.3与用于解析器编译的当前运行时版本4.7.1ANTLR运行时版本4.5.3不匹配用于代码生成的当前运行时版本4.7.1ANTLR工具版本4.5.3与用于解析器的当前运行时版本4.7.1ANTLR运行时版本4.5.3不匹配编译与当前运行时版本4.7.1C不匹配: \Users\jjlog\AndroidStudioProjects\PointMax\app\build\tmp\kapt3\stubs\debug\com\example\android\PointMax\database\CreditCards.java:12:错误:查询有问题:[SQLITE\u error]SQL错误或缺少数据库没有这样的表:Category private final java.util.List categories=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的entities部分了? 公共抽象java.lang.ObjectinsertCategory@org.jetbrains.annotations.NotNull [警告]已请求增量注释处理,但由于以下处理器不是增量的,因此禁用了支持:androidx.room.RoomProcessor DYNAMIC 任务:应用程序: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.InvocationTargetExceptionAndroid Kotlin-使用一对多实体关系时出错,android,database,sqlite,kotlin,android-room,Android,Database,Sqlite,Kotlin,Android Room,当我最初构建我的应用程序时,它采用了cardName并将其托管在一个RecyclerView中,该视图按名称保存了每张卡的列表。因为我还是开发新手,所以我这样做是为了看看我是否可以让RecyclerView只处理名称。现在,我希望能够在一对多的关系中为每张卡添加类别,我按照下面的说明进行了操作,但我可能遗漏了一些内容,因为我遇到了下面的错误。理想情况下,应用程序应该和以前一样工作,只显示名称,但现在也有额外的值,随着时间的推移,我可以慢慢添加到片段中。我非常感谢任何帮助,因为我可能忽略了一些事情
由以下原因引起:org.jetbrains.kotlin.kapt3.base.util.kaptbase错误:注释处理时出错我认为,问题出在您的DAO中:
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(card: CreditCards)
信用卡-只是一个数据类,而不是房间的实体。没有与之关联的SQLite表。因此,您不能使用它来插入。假设这个类对于房间是只读的,它在查询期间只动态地填充数据,您只能在查询中作为结果的类型使用它
您还有另外两个实体-卡片和类别,它们中的每一个都与SQLite表相关联。您应该为它们实现@Insert方法。在其中一个或两个表中对这些表进行更改后,您将通过查询得到新的结果,结果为CreditCards值 运行./gradlew clean build-stacktrace并在此处发布相关错误。更新以添加stacktrace错误。感谢您澄清Android如何获取实体值以及我如何无法通过插入将值输入数据类。但是,我仍然看到上面与categories=null相关的错误。是否有一个原因类别被设置为空,即使我以与卡片中相同的方式将值插入其中?再次感谢你的帮助;非常感谢。房间的堆栈跟踪非常有用,您所需要的就是:。。。可能您忘记将com.example.android.pointmax.database.Category添加到@database?..的实体部分:非常感谢你的帮助。因为我添加了一个新实体,所以我需要更新CardRoomDatabase的entities部分,使其具有arrayOfCard::class、Category::class。现在,我知道只在数据库中查找实体的插入,并在@database中声明新实体。谢谢你的帮助!
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)
}
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)
}
}
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)
}
}
}
}
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)
}
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)
}
}
<?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>
<?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>
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
}
}
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(card: CreditCards)