본문 바로가기
개인공부/android

[Kotlin] Room을 사용하여 로컬 데이터베이스 저장

by 왕큰새 2022. 3. 6.
728x90

Room 이란?

SQLite를 완벽히 활용하면서 데이터베이스 액세스가 가능하도록 SQLite에 추상화 계층을 제공하는 라이브러리

 

앱을 실행하는 기기에서 앱 데이터의 캐시를 만들 수 있고, 앱의 캐시를 통해 사용자는 인터넷 연결 여부와 관계없이

앱의 주요 정보를 확인할 수 있다.

Room을 사용하면 얻을수 있는 이점

  • SQL 쿼리의 컴파일 시간 확인
  • 반복적이고 오류가 발생하기 쉬운 상용구 코드를 최소화하는 편의 주석
  • 간소화된 데이터베이스 이전 경로

Room의 기본 요소

  • 데이터베이스 클래스: 데이터베이스를 보유하고 앱의 영구 데이터와의 기본 연결을 위한 기본 액세스 포인트 역할을 합니다.
  • 데이터 항목: 앱 데이터베이스의 테이블을 나타냅니다.
  • 데이터 액세스 객체(DAO): 앱이 데이터베이스의 데이터를 쿼리, 업데이트, 삽입, 삭제하는 데 사용할 수 있는 메서드를 제공합니다.

라고 android 문서에 나와있다.

 

Room 라이브러리 아키텍쳐 다이어그램

 


데이터 항목 정의 

운동 정보를 저장하기 위한 데이터 항목을 정의해보자.

 

@Entity 어노테이션을 사용해주어야 한다.

tableName 속성을 설정해주지 않으면 class의 이름이 tableName으로 지정된다.

마찬가지로 열 이름을 변수 이름과 다르게 하려면, @ColumnInfo 어노테이션을 추가하고, name 속성을 설정한다.

 

기본 키 정의를 하려면 @Primary 어노테이션을 기본 키로 지정할 열에 추가해준다.

기본 키를 자동으로 증가시키려면 autoGenerate 속성을 true로 설정해주면 된다.

 

참고: SQLite의 테이블 및 열 이름은 대소문자를 구분하지 않는다.

@Entity(tableName = "exercises")
data class Exercise(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "id") var exerciseId:Long?,
    var name: String,
    var numberOfSet: Int,
)

 


데이터 액세스 객체(DAO)

DAO란?

Data Access Object

앱 데이터베이스에 관한 추상 액세스 권한을 제공하는 메서드가 포함된 객체

즉, 데이터에 접근하기 위해 사용하는 객체

 

DAO 메서드의 두가지 유형

  • 편의 메서드: SQL 코드 작성 없이 데이터베이스에서 행을 삽입하고 업데이트하고 삭제할 수 있습니다.
  • 쿼리 메서드: 자체 SQL 쿼리를 작성하여 데이터베이스와 상호작용할 수 있습니다.

라고 android 문서에 나와있다.

 

 

편의 메서드

삽입(@Insert)

데이터베이스의 테이블에 매개변수를 삽입하는 메서드를 정의할 수 있다.

 

@Insert 메서드의 각 매개변수는

@Entity 어노테이션이 달린 Room 데이터 항목 클래스이거나, 

데이터 항목 클래스 인스턴스의 컬렉션이어야 한다. 

 

참고: @Insert 메서드가 단일 매개변수를 수신하면 Long 값을 반환할 수 있고 이 값은 삽입된 항목의 새 rowId 이다.

 

충돌 처리방식을 설정하려면 onConfilct 속성을 설정해준다.

OnConflictStrategy.REPLACE 로 설정하면, 충돌이 일어났을 때, 새로운 값으로 변경한다는 뜻이다. 

다른 충돌 처리방식 속성은 문서를 확인하자.

 

http:// https://developer.android.com/reference/android/arch/persistence/room/OnConflictStrategy

 

@Dao
interface ExerciseDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insertExercise(exercise: Exercise):Long
}

 

업데이트(@Update)

@Update 어노테이션을 사용하면 테이블에서 특정 행을 업데이트 하는 메서드를 정의할 수 있다.

@Insert 메서드와 동일하게, 데이터 항목 인스턴스( Ex) 위에서 정의한 Exercise data Class ) 를 매개변수로 허용한다.

 

기본 키(@Primary)를 사용하여 전달된 항목 인스턴스를 데이터베이스의 행과 일치시킨다.

기본 키가 같은 행이 없으면, Room에서는 아무것도 변경하지 않는다.

 

참고 : @Update 메서드는 성공적으로 업데이트된 행 수를 나타내는 int 값을 선택적으로 반환할 수 있다.

 

@Dao
interface ExerciseDao {
    @Update
    fun updateExercise(exercise: Exercise)
}

 

삭제(@Delete)

@Delete 어노테이션을 사용하면 테이블에서 특정 행을 삭제하는 메서드를 정의할 수 있다.

다른 편의 메소드들과 동일하게, 데이터 항목 인스턴스를 매개변수로 허용한다. 

 

기본 키(@Primary)를 사용하여 전달된 항목 인스턴스를 데이터베이스의 행과 일치시킨다.

기본 키가 같은 행이 없으면, Room에서는 아무것도 변경하지 않는다.

 

참고: @Delete 메서드는 성공적으로 삭제된 행 수를 나타내는 int 값을 선택적으로 반환할 수 있다.

 

 

쿼리 메서드

@Query 어노테이션을 사용하여 DAO 메서드로 사용할 수 있다. 

쿼리 메서드는 데이트베이스에서 좀 더 복잡한 삽입, 업데이트, 삭제를 실행해야 할 때 사용한다.

 

Room은 컴파일 시간에 SQL 쿼리를 검증한다. 즉, 쿼리에 문제가 있으면 런타임 실패가 아닌 컴파일 오류가 발생한다.

 

이런 형태로 필요한 메서드를 정의하여 사용할 수 있다. 

@Query("SELECT * FROM exercises")
fun getExercise(): LiveData<List<Exercise>>

@Query("SELECT * FROM exercises where exerciseId = :id")
suspend fun getExerciseById(id:Long):Exercise

@Transaction
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun createExercise(exercise: Exercise, sets: List<ExerciseSet>):Long{
    val exerciseId = insertExercise(exercise)
    sets.map {
        it.apply { parentExerciseId = exerciseId }
    }.also { insertSets(it) }
    return exerciseId
}

세번째 메서드인 createExercise 메서드는 1:N RelationShip 관계를 가진 테이블 두개에 한번에 Insert하는 메서드인데, 이 메서드와 Room에서 객체 간 관계를 정의하는 법은 글을 따로 추가하도록 할 것이다.

 

데이터베이스

AppDatabase 클래스를 정의할 것이다. 이 클래스는 데이터베이스 구성을 정의하고 영구 데이터에 대한 

앱의 기본 액세스 포인트 역할을 한다.

 

데이터베이스 클래스는 다음 조건을 충족해야 한다.

 

  • 클래스에는 데이터베이스와 연결된 데이터 항목을 모두 나열하는 entities 배열이 포함된 @Database 어노테이션(주석)이 달려야 합니다.
  • 클래스는 RoomDatabase를 확장하는 추상 클래스여야 합니다.
  • 데이터베이스와 연결된 각 DAO 클래스에서 데이터베이스 클래스는 인수가 0개이고 DAO 클래스의 인스턴스를 반환하는 추상 메서드를 정의해야 합니다.

 

라고 Android 공식 문서에 나와있다.

 

@Database(entities = [Exercise::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun ExerciseDao(): ExerciseDao
}

 

참고: 앱이 단일 프로세스에서 실행되면 AppDatabase 객체를 인스턴스화할 때 싱글톤 디자인 패턴을 따라야 합니다. 각 RoomDatabase 인스턴스는 리소스를 상당히 많이 소비하며 단일 프로세스 내에서 여러 인스턴스에 액세스해야 하는 경우는 거의 없습니다.

앱이 여러 프로세스에서 실행되는 경우 데이터베이스 빌더 호출에 enableMultiInstanceInvalidation()을 포함하세요. 이렇게 하면 각 프로세스에 AppDatabase 인스턴스가 있을 때 한 프로세스에서 공유 데이터베이스 파일을 무효화할 수 있으며 이 무효화는 다른 프로세스 내의 AppDatabase 인스턴스로 자동 전파됩니다.