Room
은 안드로이드 Jetpack
이 제공하는 데이터베이스 아키텍처이다.
쉽게 말해서 스마트폰 내장 DB에 데이터를 저장하기 위해서 사용하는 라이브러리이다.
기존에는 SQLite
라는 DB엔진을 이용해 데이터를 저장해왔으나 각종 단점들 때문에 사용하기 어려웠는데, 이 문제들을 자동으로 처리하게 도와주려 나온 녀석이 바로 Room
이다.
Room
은 SQLite
를 완벽히 활용하며, 강력한 데이터베이스 엑세스를 지원하는 추상화계층을 SQLite
에 제공한다.
위 사진에서 볼 수 있듯, Room
은 3가지 구성요소가 존재한다.
Entity
DB 내의 Table, 즉 DB에 저장할 데이터 형식으로 클래스의 변수들이 컬럼이 되어 Table이 된다.
테이블명, 칼럼명, 칼럼타입, 키본키, 외래키 등등을 정의할 수 있게 하는 다양한 어노테이션을 제공한다.
간단히 생각하면 하나의 정보단위로 사람의 이름, 나이, 번호라는 속성이 모여서 하나의 정보단위를 이루면 이것이 Entity이다.
DAO
데이터베이스에 접근하여 수행할 작업을 메소드 형태로 정의한다. 데이터 삽입, 삭제, 업데이트, 쿼리 등등을 사용할 수 있게 하는 다양한 어노테이션을 제공한다.
Room Database
데이터베이스의 전체적인 소유자 역할로 DB 생성 및 버전관리를 한다.
Recyclerview의 어댑터와 같은 느낌으로 Entity만큼 정의된 Dao 객체들을 반환할 수 있는 함수들을 가지고 추상 클래스 형태로 정의한다.
Room DB
에서Dao
를 가져오고Dao
를 사용하여Entity
에 접근한 뒤,Dao
의 메서드를 사용하여Entity
의 항목들을 CRUD 한다 정도로 이해할 수 있다.
이제 직접 사용해보자.
일단 언제나 새롭게 뭘 시작할땐 Gradle
설정부터 해줘야한다.
plugins {
id `kotlin-kapt'
}
...
val roomVersion = "2.4.2"
implementation 'androidx.room:room-runtime:$roomVersion'
kapt 'androidx.room:room-compiler:$roomVersion'
최신버전은 여기에서 확인할 수 있다.
@Entity
data class Person (
var name: String,
var age: String,
var address: String
){
@PrimaryKey(autoGenerate = true) var id: Int = 0
}
data class
를 하나 생성해준뒤 @Entity
어노테이션을 붙여주고 변수 이름과 타입을 지정한다.
primary key
는 키 값이기에 유일한 값이어야 한다. 직접 지정해도 되지만 authGenerate
를 true로 주면 자동으로 값을 생성한다.
@Embedded
라는 어노테이션을 사용하기도 하는데 이건 다른 데이터 클래스를 가져와서 하나에서 쓰기 위함이다.
street
이라는 이름을 가진 다른 data class
를 지금의 person
이라는 data class
에서 사용하고자 할때 @Embedded val street: street
이런식으로 사용해주면 사용이 가능하다
@Dao
interface PersonDao {
@Insert
fun insert(person: Person)
@Update
fun update(person: Person)
@Delete
fun delete(person: Person)
@Query("SELECT * FROM Person") // 테이블의 모든 값을 가져와라
fun getAll(): List<Person>
@Query("DELETE FROM Person WHERE name = :name") // 'name'에 해당하는 유저를 삭제해라
fun deletePersonByName(name: String)
@Insert(onConfilct = OnConfilctStrategy.REPLACE) // 중복 시 덮어쓰기
fun insertPerson(person: Person)
}
Interface
를 하나 생성해준뒤 @Dao
어노테이션을 붙여주고 그 안에 메서드를 정의한다.
메서드마다 붙은 어노테이션은 이전에 Retrofit에서 다뤘던 CRUD
의 그것과 동일하다.
@Query
를 사용하면 어떤 동작을 할껀지 sql문으로 지정을 해줄 수 있다.
@Database(entities = [Person::class], version = 1)
abstract class PersonDatabase: RoomDatabase() {
abstract fun personDao(): PersonDao
companion object {
private var instance: PersonDatabase? = null
@Synchronized
fun getInstance(context: Context): PersonDatabase? {
if (instance == null) {
synchronized(PersonDatabase::class){
instance = Room.databaseBuilder(
context.applicationContext,
PersonDatabase::class.java,
"Person-database"
).build()
}
}
return instance
}
}
}
데이터베이스를 생성하고 관리하는 데이터베이스 객체를 만들기 위해서 추상 클래스를 만들어야 한다. RoomDatabase
클래스를 상속받고, @Database
어노테이션으로 데이터베이스임을 표시한다.
@Database
우측 괄호안의 entities
에는 아까 만든 entity를 넣어주면 된다.
만약 entity가 여러개라면 (entities = arrayOf(Person::class, Home::class)
처럼 콤마로 구분하여 arrayOf()
안에 넣어주면 된다.
안드로이드 공식문서를 보면 데이터베이스 객체를 인스턴스 할 때 싱글톤으로 구현하는 것을 권장한다. 객체 생성에 비용이 많이 들기 때문이다.
따라서 companion object
로 객체를 선언해서 사용하면 된다.
var newPerson = Person("홍길동", "26", "서울시 강남구")
val db = PersonDatabase.getInstance(applicationContext)
db!!.PersonDao().insert(newPerson)
이런 식으로 데이터를 정의해두고 insert를 하면 newPerson이 데이터베이스에 추가된다.
하지만 이런식으로 실행하면 "Cannot access database on the main thread since it may potentially lock the UI for a long period of time" 라는 오류가 발생하는데,
"오래 걸리는 작업이니까 바쁜 나 말고 다른 애 시켜" 정도로 해석하면 된다.
여기서 저번에 공부했던 Coroutine
으로 비동기 처리를 하면 된다.
val db = PersonDatabase.getInstance(applicationContext)
CoroutineScope(Dispatchers.IO).launch { // 다른애 한테 일 시키기
db!!.PersonDao().insert(newPerson)
}
내용 및 사진 출처, 이해를 도와준 분들 🙏 ( 클릭 시 이동됩니다 )