与房间访问Android中的SQLite数据的问题。 MVVM架构。 Livedata

发布于 2025-02-09 14:14:09 字数 5031 浏览 1 评论 0 原文

以下代码是Android的Kotlin中基于MVVM架构的房间实现。

我对数据库的预先填充有问题。我的数据库没有任何数据,也找不到获取或插入数据的解决方案。

IDE不会提出任何汇编问题。

我认为问题是在ViewModel中,但我处于死胡同。

编辑:我看过DB,人口稠密。插入很好,但不是读取的数据。我将尝试修改ViewModel及其关联的 livedata mutableLive 数据。

这是我的数据类:

@Entity(tableName = Constants.DATABASE_NAME)
data class Card(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = Constants.COLUMN_NAME_ID) val id: Int?,
    @ColumnInfo(name = Constants.COLUMN_NAME_FAV) val fav: Boolean,
    @ColumnInfo(name = Constants.COLUMN_NAME_DATE) val date: Date,
    @ColumnInfo(name = Constants.COLUMN_NAME_RAW_VALUE) val rawValue: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_TYPE) val storeType: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_NAME) val storeName: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_NOTES) val storeNotes: String
)

这是我的接口dao

@Insert
suspend fun insertNewCard(card: Card) {
}

@Delete
suspend fun deleteCard(card: Card)

@Query("SELECT * FROM ${Constants.DATABASE_NAME}")
fun getAllCards(): LiveData<List<Card>>
  • 更新: 这是我的数据库类
@Database(
    entities = [Card::class],
    version = 1,
    exportSchema = true,
    //autoMigrations = [AutoMigration (from = 1, to = 2)]
)
@TypeConverters(Converters::class)

abstract class CardsDatabase : RoomDatabase() {

    abstract fun cardsDao(): CardsDao

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

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

        suspend fun populateDatabase(cardsDao: CardsDao) {
            val testCard = Card(null,
                false,
                ((Calendar.getInstance()).time),
                "test",
                "codebar test 1",
                "Prueba tienda",
                "Esta es una tienda de prueba"
            )
            val testCardTwo = Card(null,
                false,
                ((Calendar.getInstance()).time),
                "barcode test 2",
                "codebar",
                "Prueba tienda 2",
                "Esta es una segunda prueba de tienda"
            )
            Log.d("ROOM", "$testCard")
            cardsDao.insertNewCard(testCard)
        }
    }

    companion object {
        @Volatile
        private var INSTANCE: CardsDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): CardsDatabase {
            return INSTANCE ?: synchronized(this) {
                val  instance = Room.databaseBuilder(
                    context.applicationContext,
                    CardsDatabase::class.java,
                    Constants.DATABASE_NAME)
                    .addCallback(CardsDatabaseCallback(scope))
                    .build()
                INSTANCE = instance
                instance
            }

        }
    }

这是存储库类

val allCards: LiveData<List<Card>> = cardsDao.getAllCards()

@Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun insertCard(card: Card) {
    cardsDao.insertNewCard(card)
}

suspend fun deleteCard(card: Card) {
    cardsDao.deleteCard(card)
}

这是我的ViewModel:

LogCat在这一点上显示为Null livedata。

private val _allCards = MutableLiveData<List<Card>>()

    val allCards : LiveData<List<Card>> = _allCards

    init {
        getAllCards()
        Log.d("ROOM", "${allCards.value}")
    }

    private fun getAllCards() = viewModelScope.launch {

        _allCards.value = cardRepository.allCards.value

        Log.d("ROOM", "_ : ${_allCards.value}")
    }

    fun insertCard(card: Card) = viewModelScope.launch {
        cardRepository.insertCard(card)
        Log.d("ROOM", "inserted: $card")

    }

}

class CardsViewModelFactory(private val repository: CardRepository) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(CardsViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return CardsViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }

这是应用程序类别:

private val applicationScope = CoroutineScope(SupervisorJob())

private val database by lazy { CardsDatabase.getDatabase(this, applicationScope) }
val repository by lazy { CardRepository(database.cardsDao()) }

最后,在主动行动中观察到:

cardsViewModel.allCards.observe(this) { cards ->
        cards?.let { adapter.setData(it) }
}

The following code is an MVVM architecture-based ROOM implementation in Kotlin for Android.

I have a problem with the pre-population of the database. My database doesn't have any data or I can't find the solution to get or insert data.

The IDE doesn't throw any compilation problems.

I think the problem is in the ViewModel but I'm at a dead end.

EDITED: I have seen the DB and it's populated. The insert is fine but not the data read. I will try to modify the ViewModel and its associated livedata and mutablelive data.

This is my data class:

@Entity(tableName = Constants.DATABASE_NAME)
data class Card(
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = Constants.COLUMN_NAME_ID) val id: Int?,
    @ColumnInfo(name = Constants.COLUMN_NAME_FAV) val fav: Boolean,
    @ColumnInfo(name = Constants.COLUMN_NAME_DATE) val date: Date,
    @ColumnInfo(name = Constants.COLUMN_NAME_RAW_VALUE) val rawValue: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_TYPE) val storeType: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_NAME) val storeName: String,
    @ColumnInfo(name = Constants.COLUMN_NAME_STORE_NOTES) val storeNotes: String
)

This is my interface dao

@Insert
suspend fun insertNewCard(card: Card) {
}

@Delete
suspend fun deleteCard(card: Card)

@Query("SELECT * FROM ${Constants.DATABASE_NAME}")
fun getAllCards(): LiveData<List<Card>>
  • UPDATE:
    This is my Database class
@Database(
    entities = [Card::class],
    version = 1,
    exportSchema = true,
    //autoMigrations = [AutoMigration (from = 1, to = 2)]
)
@TypeConverters(Converters::class)

abstract class CardsDatabase : RoomDatabase() {

    abstract fun cardsDao(): CardsDao

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

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

        suspend fun populateDatabase(cardsDao: CardsDao) {
            val testCard = Card(null,
                false,
                ((Calendar.getInstance()).time),
                "test",
                "codebar test 1",
                "Prueba tienda",
                "Esta es una tienda de prueba"
            )
            val testCardTwo = Card(null,
                false,
                ((Calendar.getInstance()).time),
                "barcode test 2",
                "codebar",
                "Prueba tienda 2",
                "Esta es una segunda prueba de tienda"
            )
            Log.d("ROOM", "$testCard")
            cardsDao.insertNewCard(testCard)
        }
    }

    companion object {
        @Volatile
        private var INSTANCE: CardsDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): CardsDatabase {
            return INSTANCE ?: synchronized(this) {
                val  instance = Room.databaseBuilder(
                    context.applicationContext,
                    CardsDatabase::class.java,
                    Constants.DATABASE_NAME)
                    .addCallback(CardsDatabaseCallback(scope))
                    .build()
                INSTANCE = instance
                instance
            }

        }
    }

This is the repository class

val allCards: LiveData<List<Card>> = cardsDao.getAllCards()

@Suppress("RedundantSuspendModifier")
@WorkerThread
suspend fun insertCard(card: Card) {
    cardsDao.insertNewCard(card)
}

suspend fun deleteCard(card: Card) {
    cardsDao.deleteCard(card)
}

This is my viewModel:

The Logcat shows as null the LiveData at this point.

private val _allCards = MutableLiveData<List<Card>>()

    val allCards : LiveData<List<Card>> = _allCards

    init {
        getAllCards()
        Log.d("ROOM", "${allCards.value}")
    }

    private fun getAllCards() = viewModelScope.launch {

        _allCards.value = cardRepository.allCards.value

        Log.d("ROOM", "_ : ${_allCards.value}")
    }

    fun insertCard(card: Card) = viewModelScope.launch {
        cardRepository.insertCard(card)
        Log.d("ROOM", "inserted: $card")

    }

}

class CardsViewModelFactory(private val repository: CardRepository) :
    ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(CardsViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return CardsViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }

This is the application class:

private val applicationScope = CoroutineScope(SupervisorJob())

private val database by lazy { CardsDatabase.getDatabase(this, applicationScope) }
val repository by lazy { CardRepository(database.cardsDao()) }

For last, the observe in MainActivity:

cardsViewModel.allCards.observe(this) { cards ->
        cards?.let { adapter.setData(it) }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

撩动你心 2025-02-16 14:14:10

我的版本。

它可以与 classPath'org.jetbrains.kotlin:Kotlin-Gradle-Plugin:1.6.21'

添加到 build> build.gradle

plugins {
   id 'kotlin-kapt'
} 

dependencies {
   def room_version = "2.4.2"
   implementation "androidx.room:room-runtime:$room_version"
   kapt "androidx.room:room-compiler:$room_version"
   implementation "androidx.room:room-ktx:$room_version"
   testImplementation "androidx.room:room-testing:$room_version"
}

创建 entity.class:

@Entity(tableName = "cards")
data class CardEntity(
   @PrimaryKey(autoGenerate = true)
   val id: Int = 0,
   val data: String
)

创建 dao.class

@Dao
interface CardDAO {

   @Insert
   suspend fun insert(cardEntity: CardEntity): Long

   @Delete
   suspend fun delete(cardEntity: CardEntity): Int

   @Update
   suspend fun update(cardEntity: CardEntity): Int

   @Query("SELECT * FROM cards WHERE id = :id")
   fun getDataById(id:Int): Flow<CardEntity?>

   @Query("SELECT * FROM cards")
   fun getAll(): Flow<List<CardEntity>>

}

创建 database.class
对于工作,您应该使用Singleton

@Database(
   entities = [
       CardEntity::class
   ],
   version = 1
)
abstract class MyDatabase : RoomDatabase() {

   companion object {
       private var INSTANCE: MyDatabase? = null

       fun get(context: Context): MyDatabase {
           if (INSTANCE == null) {
               INSTANCE = Room.databaseBuilder(context, MyDatabase::class.java, 
               "testBase").build()
           }
           return INSTANCE as MyDatabase
       }
   }

   abstract fun cardDAO(): CardDAO

}

Test testDatabase.class

@RunWith(AndroidJUnit4::class)
class TestDatabase {

   lateinit var db: MyDatabase

   @Before
   fun createDB() {
       val appContext = 
       InstrumentationRegistry.getInstrumentation().targetContext
       db = Room.inMemoryDatabaseBuilder(appContext, 
       MyDatabase::class.java).build()
   }

   @After
   @Throws(IOException::class)
   fun closeDB() {
       db.close()
   }

   private fun setData() = runBlocking {
       db.cardDAO().insert(CardEntity(data = "1"))
       db.cardDAO().insert(CardEntity(data = "2"))
       db.cardDAO().insert(CardEntity(data = "3"))
       db.cardDAO().insert(CardEntity(data = "4"))
       db.cardDAO().insert(CardEntity(data = "5"))
   }

   @Test
   fun test() = runBlocking {
       setData()

       Assert.assertEquals(db.cardDAO().getAll().first().size, 5)
       val flowIdFirst = db.cardDAO().getDataById(1)
       Assert.assertEquals(flowIdFirst.first()?.data, "1")
       Assert.assertEquals(db.cardDAO().getDataById(2).first()?.data, "2")
       Assert.assertEquals(db.cardDAO().getDataById(3).first()?.data, "3")
       Assert.assertEquals(db.cardDAO().getDataById(4).first()?.data, "4")
       Assert.assertEquals(db.cardDAO().getDataById(5).first()?.data, "5")
       Assert.assertTrue(db.cardDAO().update(CardEntity(1, "my new data")) > 0)
       Assert.assertEquals(flowIdFirst.first()?.data, "my new data")
       Assert.assertTrue(db.cardDAO().delete(CardEntity(1,"")) > 0)
       Assert.assertEquals(flowIdFirst.first(), null)
   }
}

My version.

It works with classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21'

add to build.gradle:

plugins {
   id 'kotlin-kapt'
} 

dependencies {
   def room_version = "2.4.2"
   implementation "androidx.room:room-runtime:$room_version"
   kapt "androidx.room:room-compiler:$room_version"
   implementation "androidx.room:room-ktx:$room_version"
   testImplementation "androidx.room:room-testing:$room_version"
}

create Entity.class:

@Entity(tableName = "cards")
data class CardEntity(
   @PrimaryKey(autoGenerate = true)
   val id: Int = 0,
   val data: String
)

create Dao.class

@Dao
interface CardDAO {

   @Insert
   suspend fun insert(cardEntity: CardEntity): Long

   @Delete
   suspend fun delete(cardEntity: CardEntity): Int

   @Update
   suspend fun update(cardEntity: CardEntity): Int

   @Query("SELECT * FROM cards WHERE id = :id")
   fun getDataById(id:Int): Flow<CardEntity?>

   @Query("SELECT * FROM cards")
   fun getAll(): Flow<List<CardEntity>>

}

create Database.class:
for work you should to use singleton

@Database(
   entities = [
       CardEntity::class
   ],
   version = 1
)
abstract class MyDatabase : RoomDatabase() {

   companion object {
       private var INSTANCE: MyDatabase? = null

       fun get(context: Context): MyDatabase {
           if (INSTANCE == null) {
               INSTANCE = Room.databaseBuilder(context, MyDatabase::class.java, 
               "testBase").build()
           }
           return INSTANCE as MyDatabase
       }
   }

   abstract fun cardDAO(): CardDAO

}

test TestDatabase.class:

@RunWith(AndroidJUnit4::class)
class TestDatabase {

   lateinit var db: MyDatabase

   @Before
   fun createDB() {
       val appContext = 
       InstrumentationRegistry.getInstrumentation().targetContext
       db = Room.inMemoryDatabaseBuilder(appContext, 
       MyDatabase::class.java).build()
   }

   @After
   @Throws(IOException::class)
   fun closeDB() {
       db.close()
   }

   private fun setData() = runBlocking {
       db.cardDAO().insert(CardEntity(data = "1"))
       db.cardDAO().insert(CardEntity(data = "2"))
       db.cardDAO().insert(CardEntity(data = "3"))
       db.cardDAO().insert(CardEntity(data = "4"))
       db.cardDAO().insert(CardEntity(data = "5"))
   }

   @Test
   fun test() = runBlocking {
       setData()

       Assert.assertEquals(db.cardDAO().getAll().first().size, 5)
       val flowIdFirst = db.cardDAO().getDataById(1)
       Assert.assertEquals(flowIdFirst.first()?.data, "1")
       Assert.assertEquals(db.cardDAO().getDataById(2).first()?.data, "2")
       Assert.assertEquals(db.cardDAO().getDataById(3).first()?.data, "3")
       Assert.assertEquals(db.cardDAO().getDataById(4).first()?.data, "4")
       Assert.assertEquals(db.cardDAO().getDataById(5).first()?.data, "5")
       Assert.assertTrue(db.cardDAO().update(CardEntity(1, "my new data")) > 0)
       Assert.assertEquals(flowIdFirst.first()?.data, "my new data")
       Assert.assertTrue(db.cardDAO().delete(CardEntity(1,"")) > 0)
       Assert.assertEquals(flowIdFirst.first(), null)
   }
}
最好是你 2025-02-16 14:14:10

问题是在Dao中创建的Livedata。要修复它:

  • 从dao中删除Livedata以返回列表:
@Query("SELECT * FROM ${Constants.DATABASE_NAME}")
 suspend fun getAllCards(): List<Card>
  • 从存储库中,使悬挂式有趣的乐趣可以访问DAO并获取列表,然后删除Livedata类型。
suspend fun getAllCards() : List<Card> = cardsDao.getAllCards()
  • 更改访问ViewModel中存储库的方法。它可以正常工作。
private fun getAllCards() = viewModelScope.launch {
        _allCards.value = cardRepository.getAllCards()
 }  

已解决。

The problem is the LiveData created in DAO. To fix it:

  • Remove LiveData from DAO to return a List:
@Query("SELECT * FROM ${Constants.DATABASE_NAME}")
 suspend fun getAllCards(): List<Card>
  • From the repository, make a suspend fun that gets access to DAO and gets the List, and remove the LiveData type.
suspend fun getAllCards() : List<Card> = cardsDao.getAllCards()
  • Change the method to access the repository in ViewModel. It works fine.
private fun getAllCards() = viewModelScope.launch {
        _allCards.value = cardRepository.getAllCards()
 }  

Solved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文