gpt4 book ai didi

android - 关系房间数据库 : The class must be either entity or database view

转载 作者:行者123 更新时间:2023-12-05 00:13:09 32 4
gpt4 key购买 nike

我想了解如何将 Room 与关系表一起使用。我创建了一个 Job 模型,它有一个位置列表,因此需要 Job 和 Location 对象之间的一对多关系。为此,我创建了一个 JobWrapper 数据类来保存作业和位置。但是,在构建时出现以下错误:

The class must be either @Entity or @DatabaseView. -java.util.Collectionerror: Entities and POJOs must have a usablepublic constructor. You can have an empty constructor or a constructorwhose parameters match the fields (by name and type). -java.util.Collection \models\JobWrapper.java:12: error: Cannot findthe child entity column parentId in java.util.Collection.Options:private java.util.Collection<models.Location> locations;public final class JobWrapper {^ Tried the following constructors but they failed to match:
JobWrapper(models.Job,java.util.Collection<models.Location>)-> [param:job -> matched field:job, param:locations -> matched field:unmatched] models\JobWrapper.java:9:error: Cannot find setter for field.

我注意到它至少找不到位置表。但是,我不知道如何处理这个问题。从数据库读取时没有出现问题 - 它第一次出现是在我尝试使用 JobDAO 将数据放入数据库时​​出现的。我已经花了一天时间尝试解决它,因此正在寻找解决方案或关于如何解决它的一些建议。

注意:我一直遵循以下指南:

  1. https://developer.android.com/training/data-storage/room/relationships#one-to-many
  2. https://dev.to/normanaspx/android-room-how-works-one-to-many-relationship-example-5ad0

以下是我项目中的一些相关代码 fragment :

JobWrapper.kt

data class JobWrapper(
@Embedded val job: Job,

@Relation(
parentColumn = "jobid",
entityColumn = "parentId"
) var locations : Collection<Location>
)

工作

@Entity
data class Job (
@PrimaryKey
@NonNull
var jobid : String,

@NonNull
@ColumnInfo(name = "job_status")
var status : JobStatus,

@NonNull
@SerializedName("createdByAuth0Id")
var creator : String,

@SerializedName("note")
var note : String?,

@NonNull
var organisationId : String,

@NonNull
var type : JobType,

@SerializedName("atCustomerId")
@NonNull
@ColumnInfo(name = "working_at_customer_id")
var workingAtCustomerId : String,

@SerializedName("toCustomerId")
@NonNull
@ColumnInfo(name = "working_to_customer_id")
var workingToCustomerId : String,
)

JobStatus.kt

enum class JobStatus {
CREATED,
READY,
IN_PROGRESS,
FINISHED
}

Location.kt

@Entity
data class Location (
@PrimaryKey(autoGenerate = true)
var entityId: Long,

@NonNull
var parentId: String,

@NonNull
var locationId: String,

@NonNull
var type: String
) {
constructor() : this(0, "", "", "")
}

JobDao.kt

@Dao
interface JobDAO {
@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(job: JobWrapper)

@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(jobs: List<JobWrapper>)

@Transaction
@Update
fun update(job: JobWrapper)

@Transaction
@Delete
fun delete(job: JobWrapper)

@Transaction
@Query("DELETE FROM Job")
fun deleteAll()

@Transaction
@Query("SELECT * FROM Job")
fun getAll(): LiveData<List<JobWrapper>>
}

最佳答案

正如 Kraigolas 所指出的,您只能直接使用 JobWrapper 来提取您需要通过实际的底层实体插入/删除/更新的数据。

考虑以下问题

  • (与 Kraigolas 的解决方案不同,扩展功能在 JobDao 中而不是在存储库中(swings 和 roundabouts 争论哪个更好))

  • 测试注意事项为了简洁和方便,我做了一些更改,因此您必须修改以适应。

JobDao

@Dao
interface JobDAO {

/* Core/Underlying DAO's */
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(job: Job): Long
@Insert()
fun insert(location: List<Location>): List<Long>
@Transaction
@Delete
fun delete(location: List<Location>)
@Delete
fun delete(job: Job)
@Update
fun update(job: Job)
@Update
fun update(location: List<Location>)

@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(job: JobWrapper): Long {
val rv =insert(job.job)
insert(job.locations)
return rv
}

@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAll(jobs: List<JobWrapper>) {
for (jw: JobWrapper in jobs) {
insert(jw)
}
}

@Transaction
@Update
fun update(job: JobWrapper) {
update(job.locations)
update(job.job)
}

/* Delete according to JobWrapper allowing optional deletion of locations */
@Transaction
@Delete
fun delete(job: JobWrapper, deleteChildLocations: Boolean) {
if (deleteChildLocations) {
delete(job.locations)
}
delete(job.job)
}

/* will orphan locations as is */
/* Note using Foreign Keys in Location (to Job) with ON DELETE CASCADE */
@Transaction
@Query("DELETE FROM Job")
fun deleteAll()

@Transaction
@Query("SELECT * FROM Job")
fun getAll(): List<JobWrapper>

@Transaction
@Query("SELECT * FROM job WHERE jobid = :jobid")
fun getJobWrapperByJobId(jobid: String ): JobWrapper
}
  • 可以看出,Core Dao 包括 Job 和 Location 操作
  • JobWrapper 操作调用核心操作
  • 为方便起见,我使用了列表而不是集合(也就是我只涉足 Kotlin)
  • 为方便起见,类型已更改为使用 String 而不是 JobType

正如我测试过的,使用的演示/示例如下(显然 JobDao 与上面相同)

使用的 POJO 和实体是/曾经是:-

作业包装器

 data class JobWrapper(
@Embedded val job: Job,

@Relation(
parentColumn = "jobid",
entityColumn = "parentId",
entity = Location::class
) var locations : List<Location>
)
  • 列表而不是集合

工作

@Entity
data class Job (
@PrimaryKey
@NonNull
var jobid : String,

@NonNull
@ColumnInfo(name = "job_status")
var status : String,

@NonNull
var creator : String,

var note : String?,

@NonNull
var organisationId : String,

@NonNull
var type : String,

@NonNull
@ColumnInfo(name = "working_at_customer_id")
var workingAtCustomerId : String,

@NonNull
@ColumnInfo(name = "working_to_customer_id")
var workingToCustomerId : String,
)
  • 大致相同,但为方便起见,使用字符串而不是对象

位置

@Entity(
foreignKeys = [
ForeignKey(
entity = Job::class,
parentColumns = ["jobid"],
childColumns = ["parentId"],
onUpdate = ForeignKey.CASCADE,
onDelete = ForeignKey.CASCADE
)
])
data class Location (
@PrimaryKey(autoGenerate = true)
var entityId: Long,

@NonNull
var parentId: String,

@NonNull
var locationId: String,

@NonNull
var type: String
) {
constructor() : this(0, "", "", "")
}
  • 为引用完整性添加外键以及 CASCADE 删除(除非您更改 jobid,否则实际上不需要 UPDATE CASCADE,其他更新不会级联(也不需要))

@Database 使用了 JobDatabase

@Database(entities = [Location::class,Job::class],version = 1)
abstract class JobDatabase: RoomDatabase() {
abstract fun getJobDao(): JobDAO

companion object {
var instance: JobDatabase? = null
fun getInstance(context: Context): JobDatabase {
if (instance == null) {
instance = Room.databaseBuilder(context, JobDatabase::class.java, "job.db")
.allowMainThreadQueries()
.build()
}
return instance as JobDatabase
}
}
}
  • allowMainThreadQueries 用于允许在主线程上进行测试

测试/演示 Activity MainActivity

class MainActivity : AppCompatActivity() {

lateinit var db: JobDatabase
lateinit var dao: JobDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
db = JobDatabase.getInstance(this)
dao = db.getJobDao()

var jobId: String = "Job1"
var jw = JobWrapper(
Job(
jobId,
"x",
"Fred",
"Note for Job1",
"Org1",
"A","Cust1",
"Cust1"),
listOf(
Location(0,jobId,"loc1","J"),
Location(0,jobId,"Loc2","K"),
Location(0,jobId,"Loc3","L")
)
)
dao.insert(jw)
dao.insertAll(createJobWrapperList(10))
dao.delete(dao.getJobWrapperByJobId("job6"),true)
var jobWrapper = dao.getJobWrapperByJobId("job7")
jobWrapper.job.creator = "update creator"
for (l in jobWrapper.locations) {
if (l.type == "M") l.type= "UPDATED"
}
dao.update(jobWrapper)
}

fun createJobWrapperList(numberToCreate: Int): List<JobWrapper> {
val l = mutableListOf<JobWrapper>()
for(i in 1..numberToCreate) {
var jid = "job$i"
l.add(
JobWrapper(
Job(jid,"X","Creator$i","Note for $jid","org$jid","T","custA","custT"),
arrayListOf(
Location(0,jid,"loc_$jid.1","L"),
Location(0,jid,"loc_$jid.2","M"),
Location(0,jid,"loc_$jid.3","N")
)
)
)
}
return l.toList()
}
}

这个:-

  1. 获取数据库实例和 dao。
  2. 通过单个 JobWrapper 添加工作及其位置
  3. 通过 createJobWrapperList 函数生成的 JobWrappers 列表添加 x (10) 个工作和每个工作的 3 个位置。4.删除通过 getJobWrapperByJobId 获取的 JobWrapper,包括使用 delete 删除底层位置 (true) 与 jobid“job6”关联的 JobWrapper .
  4. 获取与“job7”关联的 JobWrapper 更改创建者并将类型为“M”的位置更改为“UPDATED”(仅此一个),然后使用 update (JobWrapper) 应用更新。

警告

使用 JobWrapper 插入,因为它具有 REPLACE 冲突策略,如果它替换,将导致额外的位置,因为将始终生成 entityId。

结果

运行上面的结果:-

工作表:-

enter image description here

  • 可以看到 job6 已被删除(添加 11 行,剩下 10 行)并且 job7 的创建者已更新。

位置表:-

enter image description here

  • 可以看出没有 job6 位置(之前是 33 个位置 (11 * 3),现在是 30 个)并且类型 M 的位置已根据传递的 JobWrapper 进行了更新。

你问:-

How do I ensure the relationship to the right parent (job) when inserting the childs (locations)?

我认为以上说明了方法。

关于android - 关系房间数据库 : The class must be either entity or database view,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67843988/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com