Unit 5-2

jiwon·2022년 2월 9일
0

코틀린

목록 보기
15/16
post-thumbnail
post-custom-banner

Room의 기본 구성요소

Room의 기본 요소는 다음과 같다.

  • Data entities (데이터 항목) : 앱 데이터베이스의 테이블
  • Data Access Object (DAO) : 검색 및 업뎃, 삽입, 삭제하는 데 사용하는 메서드를 제공
  • Database class : 데이터베이스를 보유하며, 기본 앱 데이터베이스 연결을 위한 기본 액세스 포인트. DAO 인스턴스를 제공(=Room Database 인듯)

Room 구성요소가 함께 작동하여 데이터베이스와 상호작용하는 걸 그림으로 나타내면 이렇다.

항목 Entity 만들기

Entity 클래스의 각 인스턴스는 데이터베이스 테이블의 행을 나타낸다. 앱에서 Entity는 이름,가격,수량 등 Entity에 관한 정보를 보유한다.

그림을 보는게 더 이해가 잘된다.

@Entity 주석은 클래스를 데이터베이스 Entity 클래스로 표시한다.

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
data class Item(
   @PrimaryKey(autoGenerate = true)
   val id: Int = 0,
   @ColumnInfo(name = "name")
   val itemName: String,
   @ColumnInfo(name = "price")
   val itemPrice: Double,
   @ColumnInfo(name = "quantity")
   val quantityInStock: Int
)

테이블 초기화하는 이런 코드가 Entity 클래스!

항목 DAO 만들기

DAO 는 데이터베이스에 액세스하는 인터페이스를 정의하는 Room의 기본 구성요소이다. 여기서 만드는 DAO는 데이터베이스 쿼리/검색, 삽입, 삭제, 업데이트를 위한 편의 메서드를 제공하는 맞춤 인터페이스이다.

@Dao
interface ItemDao {
    //onConflict: 충돌 발생시 Room이 뭘해야하는지 지정
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(item: Item)  //정지함수로 만들어 코루틴에서 호출할 수 있게..

    @Update
    suspend fun update(item: Item)

    @Delete
    suspend fun delete(item: Item)

    //삽입,업뎃,삭제는 전용 주석(@)이 존재함.
    //근데 검색 등은 전용 주석 없으니까 @Query(쿼리문)의 형태로 써야 한다.
    @Query("SELECT * from item WHERE id = :id")
    fun getItem(id: Int): Flow<Item> //데이터 바뀌었을때 바로 대응 위해..Flow(지속성)

    //윗놈은 데이터 하나 반환, 얘는 전체 반환
    @Query("SELECT * from item ORDER BY name ASC")
    fun getItems(): Flow<List<Item>>
}

쿼리문 작성하는 이런 코드가 Entity 클래스!

데이터베이스 인스턴스 만들기

이렇게 만든 Entity와 DAO를 사용하는 RoomDatabase(데이터베이스 클래스)를 정의하자. 얘는 개발자가 정의한 DAO의 인스턴스를 앱에 제공한다.


/*
@Database는 매개변수 3개가 필요함.
entities: 아까만든 item 테이블.
version: 데이터베이스 스키마 구조 바뀔때마다 +1 해야함. 초기상태니까 1
exportSchema: false로 두면 스키마 버전 기록 백업 안함.
 */
@Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class ItemRoomDatabase : RoomDatabase() {
    //이놈이 dao를 알아야 지지고 볶고 하겠지..ItemDao를 반환하는 추상 함수를 선언.
    abstract fun itemDao(): ItemDao
    // 참고-Dao는 여러개일 수 있다.

    //데이터베이스는 만드는데 비용이 많이듦. 단일 디비를 유지하자.
    companion object {
        @Volatile
        private var INSTANCE: ItemRoomDatabase? = null
        fun getDatabase(context: Context): ItemRoomDatabase {
            //db 인스턴스가 있으면 걜 반환, 없으면 synchronized 블록 실행해서 만들기
            return INSTANCE ?: synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    ItemRoomDatabase::class.java,
                    "item_database"
                )
                    .fallbackToDestructiveMigration()
                    .build()
                INSTANCE = instance
                return instance
            }

    }
}

}

데이터베이스 인스턴스를 만들기 위한 붕어빵 틀이 준비가 되었으니... Application 클래스에서 데이터베이스 인스턴스를 인스턴스화한다.

class InventoryApplication : Application(){
    ///ItemRoomDatabase에서 getDatabase()를 호출하여 database 인스턴스를 인스턴스화
    val database: ItemRoomDatabase by lazy { ItemRoomDatabase.getDatabase(this) }
}

ViewModel 추가


이제 Room부분은 다 준비가 됐음. ViewModel 부분을 만들어야 한다.
ViewModel은 DAO를 통해 데이터베이스와 상호작용하여 UI에 데이터를 제공한다. 모든 데이터베이스 작업은 기본 UI 스레드에서 벗어나 실행되어야 함. 이를 위해 코루틴과 viewModelScope를 사용하면 된다.

//ItemDao 객체를 생성자의 매개변수로!
class InventoryViewModel(private val itemDao: ItemDao) : ViewModel() {}

//위에 있는 InventoryViewModel를 인스턴스화
class InventoryViewModelFactory(private val itemDao: ItemDao) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {

        if (modelClass.isAssignableFrom(InventoryViewModel::class.java)) {
            //이상이 없으면 InventoryViewModel 인스턴스 반환
            @Suppress("UNCHECKED_CAST")
            return InventoryViewModel(itemDao) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

아직 InventoryViewModel가 비어 있음. 이제 여기다가 원하는 함수를 만들면 된다.

//ItemDao 객체를 생성자의 매개변수로!
class InventoryViewModel(private val itemDao: ItemDao) : ViewModel() {
    private fun insertItem(item: Item) {
        //기본 스레드 밖에서 데이터베이스와 상호작용하려면 코루틴을 시작
        //ViewModelScope는 ViewModel이 소멸될 때 하위 코루틴을 자동으로 취소함.
        viewModelScope.launch {
            itemDao.insert(item)
        }
    }

    private fun getNewItemEntry(itemName: String, itemPrice: String, itemCount: String): Item {
        return Item(
            itemName = itemName,
            itemPrice = itemPrice.toDouble(),
            quantityInStock = itemCount.toInt()
        )
    }

    fun addNewItem(itemName: String, itemPrice: String, itemCount: String) {
        val newItem = getNewItemEntry(itemName, itemPrice, itemCount)
        insertItem(newItem)
    }
    fun isEntryValid(itemName: String, itemPrice: String, itemCount: String): Boolean {
   		if (itemName.isBlank() || itemPrice.isBlank() || itemCount.isBlank()) {
       		return false
   }
   return true
}
}

함수들을 추가했다! 이제 프래그먼트 등에서 가져다 쓰면 된다.

마지막으로 Database Inspector 열어서 Live update에 체크표시해주자.

profile
개발 공부합니다. 파이팅!
post-custom-banner

0개의 댓글