当我想在 Room 中存储数据时,NOT NULL 约束失败
NOT NULL constraint failed when I want to store data in Room
我尝试为我的应用建立离线支持。这是我 运行 应用程序时的异常错误:
2019-04-19 13:44:44.768 8549-8549/com.sample.android.superhero E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.sample.android.superhero, PID: 8549
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: heroes.power_id (code 1299)
#################################################################
Error Code : 1299 (SQLITE_CONSTRAINT_NOTNULL)
Caused By : Abort due to constraint violation.
(NOT NULL constraint failed: heroes.power_id (code 1299))
#################################################################
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:953)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:64)
at com.sample.android.superhero.data.source.local.HeroDao_Impl.insertHero(HeroDao_Impl.java:189)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource$saveHero.invokeSuspend(SuperHeroLocalDataSource.kt:27)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource$saveHero.invoke(Unknown Source:10)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:146)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource.saveHero(SuperHeroLocalDataSource.kt:26)
at com.sample.android.superhero.data.source.SuperHeroRepository.refreshLocalDataSource(SuperHeroRepository.kt:55)
at com.sample.android.superhero.data.source.SuperHeroRepository.getHeroesFromRemoteDataSource(SuperHeroRepository.kt:45)
at com.sample.android.superhero.data.source.SuperHeroRepository$getHeroesFromRemoteDataSource.invokeSuspend(Unknown Source:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.ResumeModeKt.resumeUninterceptedMode(ResumeMode.kt:46)
at kotlinx.coroutines.internal.ScopeCoroutine.onCompletionInternal$kotlinx_coroutines_core(Scopes.kt:28)
at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:305)
at kotlinx.coroutines.JobSupport.tryFinalizeSimpleState(JobSupport.kt:264)
at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:762)
at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:742)
at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:117)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
我的英雄模型如下:
@Entity(tableName = "heroes")
class Hero(
@PrimaryKey @ColumnInfo(name = "entryid")
val id: String,
val name: String,
@Embedded(prefix = "power_")
val power: Powerstates,
@Embedded(prefix = "bio_")
val bio: Biography,
@Embedded(prefix = "appearance_")
val appearance: Appearance,
@Embedded(prefix = "work_")
val work: Work,
@Embedded
val image: Image
)
这是我的 Power 模型:
@Entity(
tableName = "powerstates",
foreignKeys = [
ForeignKey(
entity = Hero::class,
parentColumns = ["entryid"],
childColumns = ["heroId"],
onDelete = ForeignKey.CASCADE
)], indices = [Index("heroId")]
)
class Powerstates(
@PrimaryKey(autoGenerate = true)
val id: Int,
val heroId: Int,
val intelligence: Int,
val strength: Int,
val speed: Int,
val power: Int,
val combat: Int
)
这是保存和检索数据的逻辑:
override suspend fun getHeroes(query: String): Result<List<Hero>> {
return if (cacheIsDirty) {
// If the cache is dirty we need to fetch new data from the network.
getHeroesFromRemoteDataSource(query)
} else {
// Query the local storage if available. If not, query the network.
when (val result = localDataSource.getHeroes(query)) {
is Result.Success -> Result.Success(result.data)
is Result.Error -> getHeroesFromRemoteDataSource(query)
}
}
}
private suspend fun getHeroesFromRemoteDataSource(query: String): Result<List<Hero>> {
return when (val result = remoteDataSource.getHeroes(query)) {
is Result.Success -> {
refreshLocalDataSource(result.data)
Result.Success(result.data)
}
is Result.Error -> Result.Error(result.exception)
}
}
private suspend fun refreshLocalDataSource(heroes: List<Hero>) {
localDataSource.deleteAllHeroes()
for (hero in heroes) {
localDataSource.saveHero(hero)
}
}
我尝试了以下解决方案:
但是我得到了同样的错误。
这是我的 Dao
:
@Dao
interface HeroDao {
@Query("SELECT * FROM Heroes WHERE name LIKE :query")
fun getHeroes(query: String): List<Hero>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertHero(hero: Hero)
}
您正在使用 Powerstates
作为 @Embedded
对象,而不是单独的实体。因此,从 Powerstates
中删除 @PrimaryKey
和 @Entity
,以及(显然)不必要的列。你应该结束:
class Powerstates(
val intelligence: Int,
val strength: Int,
val speed: Int,
val power: Int,
val combat: Int
)
正如 CommonsWare 提到的:您可能想查看生成的 Room 代码(类 在您的堆栈跟踪中列出)并查看可能出了什么问题。 – CommonsWare
最好调试生成的代码,您将获得所需的信息。就我而言,它有所帮助。
我尝试为我的应用建立离线支持。这是我 运行 应用程序时的异常错误:
2019-04-19 13:44:44.768 8549-8549/com.sample.android.superhero E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.sample.android.superhero, PID: 8549
android.database.sqlite.SQLiteConstraintException: NOT NULL constraint failed: heroes.power_id (code 1299)
#################################################################
Error Code : 1299 (SQLITE_CONSTRAINT_NOTNULL)
Caused By : Abort due to constraint violation.
(NOT NULL constraint failed: heroes.power_id (code 1299))
#################################################################
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:953)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at androidx.sqlite.db.framework.FrameworkSQLiteStatement.executeInsert(FrameworkSQLiteStatement.java:51)
at androidx.room.EntityInsertionAdapter.insert(EntityInsertionAdapter.java:64)
at com.sample.android.superhero.data.source.local.HeroDao_Impl.insertHero(HeroDao_Impl.java:189)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource$saveHero.invokeSuspend(SuperHeroLocalDataSource.kt:27)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource$saveHero.invoke(Unknown Source:10)
at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:146)
at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
at com.sample.android.superhero.data.source.local.SuperHeroLocalDataSource.saveHero(SuperHeroLocalDataSource.kt:26)
at com.sample.android.superhero.data.source.SuperHeroRepository.refreshLocalDataSource(SuperHeroRepository.kt:55)
at com.sample.android.superhero.data.source.SuperHeroRepository.getHeroesFromRemoteDataSource(SuperHeroRepository.kt:45)
at com.sample.android.superhero.data.source.SuperHeroRepository$getHeroesFromRemoteDataSource.invokeSuspend(Unknown Source:12)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.ResumeModeKt.resumeUninterceptedMode(ResumeMode.kt:46)
at kotlinx.coroutines.internal.ScopeCoroutine.onCompletionInternal$kotlinx_coroutines_core(Scopes.kt:28)
at kotlinx.coroutines.JobSupport.completeStateFinalization(JobSupport.kt:305)
at kotlinx.coroutines.JobSupport.tryFinalizeSimpleState(JobSupport.kt:264)
at kotlinx.coroutines.JobSupport.tryMakeCompleting(JobSupport.kt:762)
at kotlinx.coroutines.JobSupport.makeCompletingOnce$kotlinx_coroutines_core(JobSupport.kt:742)
at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:117)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:233)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:594)
at kotlinx.coroutines.scheduling.CoroutineScheduler.access$runSafely(CoroutineScheduler.kt:60)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:742)
我的英雄模型如下:
@Entity(tableName = "heroes")
class Hero(
@PrimaryKey @ColumnInfo(name = "entryid")
val id: String,
val name: String,
@Embedded(prefix = "power_")
val power: Powerstates,
@Embedded(prefix = "bio_")
val bio: Biography,
@Embedded(prefix = "appearance_")
val appearance: Appearance,
@Embedded(prefix = "work_")
val work: Work,
@Embedded
val image: Image
)
这是我的 Power 模型:
@Entity(
tableName = "powerstates",
foreignKeys = [
ForeignKey(
entity = Hero::class,
parentColumns = ["entryid"],
childColumns = ["heroId"],
onDelete = ForeignKey.CASCADE
)], indices = [Index("heroId")]
)
class Powerstates(
@PrimaryKey(autoGenerate = true)
val id: Int,
val heroId: Int,
val intelligence: Int,
val strength: Int,
val speed: Int,
val power: Int,
val combat: Int
)
这是保存和检索数据的逻辑:
override suspend fun getHeroes(query: String): Result<List<Hero>> {
return if (cacheIsDirty) {
// If the cache is dirty we need to fetch new data from the network.
getHeroesFromRemoteDataSource(query)
} else {
// Query the local storage if available. If not, query the network.
when (val result = localDataSource.getHeroes(query)) {
is Result.Success -> Result.Success(result.data)
is Result.Error -> getHeroesFromRemoteDataSource(query)
}
}
}
private suspend fun getHeroesFromRemoteDataSource(query: String): Result<List<Hero>> {
return when (val result = remoteDataSource.getHeroes(query)) {
is Result.Success -> {
refreshLocalDataSource(result.data)
Result.Success(result.data)
}
is Result.Error -> Result.Error(result.exception)
}
}
private suspend fun refreshLocalDataSource(heroes: List<Hero>) {
localDataSource.deleteAllHeroes()
for (hero in heroes) {
localDataSource.saveHero(hero)
}
}
我尝试了以下解决方案:
这是我的 Dao
:
@Dao
interface HeroDao {
@Query("SELECT * FROM Heroes WHERE name LIKE :query")
fun getHeroes(query: String): List<Hero>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertHero(hero: Hero)
}
您正在使用 Powerstates
作为 @Embedded
对象,而不是单独的实体。因此,从 Powerstates
中删除 @PrimaryKey
和 @Entity
,以及(显然)不必要的列。你应该结束:
class Powerstates(
val intelligence: Int,
val strength: Int,
val speed: Int,
val power: Int,
val combat: Int
)
正如 CommonsWare 提到的:您可能想查看生成的 Room 代码(类 在您的堆栈跟踪中列出)并查看可能出了什么问题。 – CommonsWare
最好调试生成的代码,您将获得所需的信息。就我而言,它有所帮助。