어제 알아봤던 Sheard Preference
와 같이 로컬에 데이터를 저장할수 있게 만들어준다.
SQLite
를 보다 쉽게 사용할 수 있도록 해주는 데이터베이스 객체
매핑 라이브러리 이다.
Room
을 사용하기에 앞서 데이터베이스를 조작하기위해 기본적인 데이터 베이스에 대한 용어의 정리가 필요했다.
관계형 데이터베이스
관리 시스템을 제공하는 라이브러리를 말한다.
관계형 데이터베이스
에서 데이터를 정의
, 조작
, 제어
하기위해 사용하는 표준화된 언어를 말한다.
데이터베이스에 대한 Query
를 작성할때 사용한다.
(Kotlin
이나 Java
와 같은 데이터 베이스 조작에 사용되는 언어라고 생각하면 될 듯하다.)
데이터 베이스에서 정보를 조작하거나 요청하기 위해 사용되는 문장이나 명령어를 의미한다.
즉, SQL(언어)
를 사용하여 SQLite(라이브러리)
를 사용해 데이터 베이스에 Query(명령)
를 정의해 데이터를 요청하거나 조작한다고 생각하면 쉽다.
결국 Room
이란 보다 쉽게 Query
를 사용할 수 있도록 해주는 라이브러리라고 할 수 있다.
(SQL
을 사용하지 않아도 조작이 가능하게 하는)
Room
사용시 장점으로는 크게 두가지를 꼽아볼 수 있는다.
Query
를 검증한다.컴파일 타임에서 검증을 하기때문에 런타임에서 오류가 생기는 것을 방지한다.
즉, 잘못된 Query
나 오타를 앱이 구동되기 이전에 알려주기 때문에 안정성을 높일 수 있다.
Query
결과를 LiveData
로 만들 수 있기떄문에 데이터 베이스가 변동될경우 즉시 UI를 자동으로 업데이트 할 수 있다.
Room
라이브러리 사용을 위해서는 3가지의 기본 구성요소가 필요하다.
데이터 베이스를 보유하고 앱의 영구적인 데이터와 연결되는 기본 액세스 포인트 역할을 한다.
abstract class
에 RoomDatabase
클래스를 상속해서 선언한뒤, @Database()
어노테이션을 사용해 데이터베이스 클래스를 만든다.
@Database()
어노테이션은 다양한 매개변수를 가진다.
- entities: 데이터 베이스에 포함되는 엔티티(테이블)을 나타낸다.
- exportSchema:
true
일 경우 스키마를 내보낸다.
true
일경우 스키마를 내보내며, 주로 버전 업그레이드시 유용하게 사용된다.
- version: 데이터 베이스의 버전을 나타낸다.
정수값으로 표현되며, 스키마가 변경되면 버전번호를 증가시켜 업그레이드 관리를 할 수 있다.
위의 내용들을 사용해 아래와 같이 어노테이션을 정의할 수 있다.
@Database(
entities = [Student::class], // Student클래스의 내용을 엔티티로 사용
exportSchema = false, // 스키마를 내보내지 않음
version = 1 // 버전 1 (스키마가 변경 될 경우 2로 변경)
)
앱 데이터 베이스의 테이블을 나타내는 클래스로, data class
로 생성한다.
@Entity(tableName = "student_table")
data class Student(
@PrimaryKey // 다른 값과 겹치지 않는 고유한 키를 생성
@ColumnInfo(name = "student_id") // name프로퍼티에 "student_id"로 전달받은 값을 대입
val id: Int,
val name: String
)
@Entity
어노테이션을 사용하며 ()
안에 테이블의 이름을 적어준다.
data class
내부에는 테이블에 사용될 각 항목을 만들어준다.
테이블이란 데이터 베이스에서 실제로 데이터가 저장되는 구조로, 아래와 같은 표로 이해하면 편하다.
하나의 엔티티는 일반적인 표의 가로 항목의 속성을 말한다고 할 수 있다.
앱에 데이터 베이스의 데이터를 Query
, 업데이트
, 삽입
, 삭제
하는데 필요한 메서드를 제공하는 객체이다.
interface
에 @Dao
어노테이션을 사용해서 생성하며, 내부의 메서드에 필요한 어노테이션을 사용해 Query
나 삽입
, 삭제
를 해주는 메서드를 생성한다.
@Dao
interface MyDao {
// 삽입을 해주는 메서드 student값을 삽입해주며, 같은 값이 있을 경우 대체해준다.
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertStudent(student: Student)
// 작성한 Query를 실행 아래의 경우 student_table의 모든 항목을 라이브데이터에 전달한다.
@Query("SELECT * FROM student_table")
fun getAllStudent(): LiveData<List<Student>>
// student_table를 가져와 전달받은 sname의 값과 일치하는 항목을 반환한다.
@Query("SELECT * FROM student_table WHERE name = :sname")
suspend fun getStudentByName(sname: String): List<Student>
// student의 값을 받아 삭제한다.
@Delete
suspend fun deleteStudent(student: Student)
}
@Database(
entities = [Student::class],
exportSchema = false, version = 1
)
abstract class MyDatabase : RoomDatabase() {
abstract fun getMyDao(): MyDao
companion object {
private var INSTANCE: MyDatabase? = null
fun getDatabase(context: Context): MyDatabase {
if (INSTANCE == null) {
INSTANCE = Room.databaseBuilder(context, MyDatabase::class.java, "school_database")
.build()
}
return INSTANCE as MyDatabase
}
}
}
abstract class
에 싱글톤 패턴으로 데이터 베이스를 반환하는 메서드를 만들어준다.
매서드 내에서 Room.databaseBuilder
를 사용해 매개변수로, 전달받은 context
와 데이터베이스 클래스
, 데이터 베이스의 이름을 넣어준뒤 데이터베이스를 build
하여 반환해주면 데이터 베이스가 생성된다.
abstract 메서드
로 Dao
를 반환할 수 있도록 만들어주면 준비 끝.
val myDao by lazy { MyDatabase.getDatabase(applicationContext).getMyDao() }
위와 같이 데이터베이스를 생성하고 Dao를 가지고 오면 이제 myDao
를 사용해 Query
, 삽입
, 삭제
명령을 사용하는게 가능해진다.
정말 할게 많은데 왜 난 아직도 헤어나오지 못하는가...
알면 알수록 파고드는 나란 사람...
적당히 좀 파줘... 미래의 나...