Использование комнаты в Jetpack Compose
Статья здесь: https://flatteredwithflutter.com/using-room-in-jetpack-compose/
Вступление
Уровень: начальный
Мы кратко рассмотрим:
- Использование комнаты в Jetpack Compose
- Написание CRUD-операций
- Написать тест для базы данных
Примечание. В этой статье предполагается, что читатель знает о Jetpack Compose.
Использование комнаты в Jetpack Compose
Согласно документации,
Библиотека сохраняемости Room обеспечивает уровень абстракции над SQLite, чтобы обеспечить более надежный доступ к базе данных, используя всю мощь SQLite.
Настроить комнату в Compose
Чтобы использовать Room в своем приложении, добавьте следующие зависимости в build.gradle
файл приложения:
dependencies { def room_version = "2.2.6" // FOR ROOM implementation "androidx.room:room-runtime:$room_version" kapt "androidx.room:room-compiler:$room_version" // optional - Kotlin Extensions and Coroutines support for Room implementation "androidx.room:room-ktx:$room_version" // END FOR ROOM }
Компоненты комнаты
Комната состоит из 3 основных компонентов:
- Класс базы данных, который содержит базу данных.
- Сущности данных, которые представляют таблицы в базе данных вашего приложения.
- Объекты доступа к данным (DAO) предоставляют методы, которые ваше приложение может использовать для запроса, обновления, вставки и удаления данных в базе данных.
Внутри нашего кода это выглядит так
Чтобы использовать состояние в составе реактивного ранца, обратитесь к этому
Написание CRUD-операций
Давайте посмотрим, что будет в каждом файле в базе данных (на скриншоте выше).
- TodoItem (наш класс сущности)
Мы создаем объект под названием TodoItem
- Класс аннотируется @Entity и именем таблицы.
@Entity(tableName = "my_todo_list") data class TodoItem( @PrimaryKey(autoGenerate = true) var itemId: Long = 0L, @ColumnInfo(name = "item_name") val itemName: String, @ColumnInfo(name = "is_completed") var isDone: Boolean = false )
2. TodoDatabaseDao (наши объекты доступа к данным)
Создаем интерфейс (TodoDatabaseDao)
- Класс помечен @ Dao
@Dao interface TodoDatabaseDao { @Query("SELECT * from my_todo_list") fun getAll(): LiveData<List<TodoItem>> @Query("SELECT * from my_todo_list where itemId = :id") fun getById(id: Int) : TodoItem? @Insert suspend fun insert(item:TodoItem) @Update suspend fun update(item:TodoItem) @Delete suspend fun delete(item:TodoItem) @Query("DELETE FROM my_todo_list") suspend fun deleteAllTodos() }
- Аннотация запроса используется для написания пользовательских запросов (например, прочитать или удалить все в нашем случае)
- Аннотации Insert, Update, Delete выполняют операции CUD.
- Мы отмечаем эти функции как приостановленные (чтобы мы могли вызывать их внутри сопрограмм).
3. TodoDatabase (наш класс базы данных)
Мы определяем абстрактный класс (TodoDatabase), который расширяет RoomDatabase
- Этот класс помечен @ Database
@Database(entities = [TodoItem::class], version = 1) abstract class TodoDatabase : RoomDatabase() { abstract fun todoDao(): TodoDatabaseDao companion object { private var INSTANCE: TodoDatabase? = null fun getInstance(context: Context): TodoDatabase { synchronized(this) { var instance = INSTANCE if (instance == null) { instance = Room.databaseBuilder( context.applicationContext, TodoDatabase::class.java, "todo_list_database" ).fallbackToDestructiveMigration() .build() INSTANCE = instance } return instance } } } }
- Поток, который входит в синхронизированный метод, получает блокировку, и никакой другой поток не может войти в метод, пока блокировка не будет снята. Kotlin предлагает те же функции, что и аннотация Synchronized.
4. TodoRepository (наш класс репозитория)
Мы создаем класс репозитория (TodoRepository), который принимает TodoDatabaseDao в качестве параметра конструктора.
class TodoRepository(private val todoDatabaseDao: TodoDatabaseDao) { val readAllData : LiveData<List<TodoItem>> = todoDatabaseDao.getAll() suspend fun addTodo(todoItem: TodoItem) { todoDatabaseDao.insert(todoItem) } suspend fun updateTodo(todoItem: TodoItem) { todoDatabaseDao.update(todoItem) } suspend fun deleteTodo(todoItem: TodoItem) { todoDatabaseDao.delete(todoItem) } suspend fun deleteAllTodos() { todoDatabaseDao.deleteAllTodos() } }
- Все взаимодействия с базой данных выполняются через этот уровень репозитория.
- Поскольку наши реализации были функциями приостановки, то же самое и в наших определениях.
- Мы предоставляем readAllData типа LiveData, поэтому мы можем наблюдать за изменениями и уведомлять пользовательский интерфейс.
Написать тест для базы данных
Поскольку наша база данных TodoDatabase готова, давайте протестируем ее.
- Создайте тестовый класс TodoDatabaseTest в папке androidTest.
- Мы аннотируем наш класс с помощью
AndroidJUnit4
Это то, что будет управлять тестами для одного класса.
@RunWith(AndroidJUnit4::class) class TodoDatabaseTest { private lateinit var todoDao: TodoDatabaseDao private lateinit var db: TodoDatabase @Before fun createDb() { val context = InstrumentationRegistry.getInstrumentation().targetContext db = Room.inMemoryDatabaseBuilder(context, TodoDatabase::class.java) .allowMainThreadQueries() .build() todoDao = db.todoDao() } @After @Throws(IOException::class) fun deleteDb() { db.close() } @Test @Throws(Exception::class) fun insertAndGetTodo() = runBlocking { val todoItem = TodoItem(itemId = 1, itemName = "Dummy Item", isDone = false) todoDao.insert(todoItem) val oneItem = todoDao.getById(1) assertEquals(oneItem?.itemId, 1) } }
- Перед запуском теста мы создаем базу данных с помощью createDb.
- После запуска теста мы удаляем базу данных с помощью deleteDb
- Во время выполнения теста мы вызываем insertAndGetTodo
- Поскольку это всего лишь тест, поэтому мы используем runBlocking.
- Создайте задачу и утвердите ее в соответствии с созданной ценностью.
- Нажмите Запустить TodoDatabaseTest, чтобы запустить тест.
- Если все в порядке, вы должны увидеть
Source code
Интересующие статьи: