이 자료는 Android Kotlin 기초의 내용을 정리한 것입니다.
Android에서 데이터는 데이터 클래스로 표현됩니다. 이 데이터는 함수 호출을 사용하여 액세스하고 수정할 수 있습니다. 그러나 데이터베이스 세계에서는 데이터에 액세스하고 수정하기 위해 엔터티와 쿼리가 필요합니다.
엔터티는 해당 속성과 함께 데이터베이스에 저장할 개체 또는 개념을 나타냅니다. 애플리케이션 코드에서 테이블을 정의하는 엔터티 클래스가 필요하며 해당 클래스의 각 인스턴스는 해당 테이블의 행을 나타냅니다. 엔티티 클래스에는 데이터베이스의 정보를 표시하고 상호 작용하는 방법을 Room에 알려주는 매핑이 있습니다. 앱에서 엔티티는 수면에 대한 정보를 보유할 것입니다.
쿼리는 데이터베이스 테이블 또는 테이블 조합의 데이터 또는 정보에 대한 요청 또는 데이터에 대한 작업을 수행하라는 요청입니다. 일반적인 쿼리는 엔터티를 생성, 읽기, 업데이트 및 삭제하기 위한 것입니다. 예를 들어 쿼리를 실행하여 기록에 있는 모든 수면을 시작 시간별로 정렬하여 읽을 수 있습니다.
앱의 사용자 경험(다른 일반적인 사용 사례와 유사)은 일부 데이터를 로컬에 유지하면 큰 이점을 얻을 수 있습니다. 관련 데이터를 캐싱하면 사용자가 오프라인 상태에서도 앱을 즐길 수 있습니다. 앱이 서버에 의존하는 경우 캐싱을 통해 사용자는 오프라인 상태에서 로컬로 지속되는 콘텐츠를 수정할 수 있습니다. 앱이 다시 연결되면 캐시된 변경 사항을 백그라운드에서 원활하게 서버에 동기화할 수 있습니다.
Room은 Kotlin 데이터 클래스를 SQLite 테이블에 저장할 수 있는 엔터티입니다. 또한 함수를 SQLite 쿼리로 바꿀 수도 있습니다.
각 엔터티를 주석이 있는 데이터 클래스로 정의하고 해당 엔터티와의 상호 작용을 DAO(데이터 액세스 개체)라고 하는 주석이 있는 인터페이스로 정의해야 합니다. Room은 이러한 주석이 추가된 클래스를 사용하여 데이터베이스에 테이블을 생성하고 데이터베이스에서 작동하는 쿼리를 생성합니다.
이 작업에서는 데이터베이스 엔터티를 나타내는 주석이 있는 데이터 클래스로 하루의 수면을 정의합니다.
수면에 대해 시작 시간, 종료 시간, 품질 등급을 기록해야 합니다.
그리고 날짜를 고유하게 식별할 수 있는 ID가 필요합니다.
data class SleepNight(
var nightId: Long = 0L,
val startTimeMilli: Long = System.currentTimeMillis(),
var endTimeMilli: Long = startTimeMilli,
var sleepQuality: Int = -1
)
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(...)
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,...
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "daily_sleep_quality_table")
data class SleepNight(
@PrimaryKey(autoGenerate = true)
var nightId: Long = 0L,
@ColumnInfo(name = "start_time_milli")
val startTimeMilli: Long = System.currentTimeMillis(),
@ColumnInfo(name = "end_time_milli")
var endTimeMilli: Long = startTimeMilli,
@ColumnInfo(name = "quality_rating")
var sleepQuality: Int = -1
)
이 작업에서는 DAO(데이터 액세스 개체)를 정의합니다. Android에서 DAO는 데이터베이스를 삽입, 삭제 및 업데이트하기 위한 편리한 메서드를 제공합니다.
Room 데이터베이스를 사용할 때 코드에서 Kotlin 함수를 정의하고 호출하여 데이터베이스를 쿼리합니다. 이러한 Kotlin 함수는 SQL 쿼리에 매핑됩니다. 주석을 사용하여 DAO에서 이러한 매핑을 정의하면 Room에서 필요한 코드를 생성합니다.
DAO를 데이터베이스에 액세스하기 위한 사용자 정의 인터페이스를 정의하는 것으로 생각하십시오.
일반적인 데이터베이스 작업의 경우 Room 라이브러리는 @Insert, @Delete 및 @Update와 같은 편리한 주석을 제공합니다. 다른 모든 것에는 @Query 주석이 있습니다. SQLite에서 지원하는 모든 쿼리를 작성할 수 있습니다.
Android Studio에서 쿼리를 생성하면 컴파일러가 SQL 쿼리에서 구문 오류를 확인합니다.
sleep nights의 sleep-tracker 데이터베이스의 경우 다음을 수행할 수 있어야 합니다.
@Dao
interface SleepDatabaseDao {}
Room은 SleepNight를 데이터베이스에 삽입하는 데 필요한 모든 코드를 생성합니다. Kotlin 코드에서 insert()를 호출하면 Room은 SQL 쿼리를 실행하여 엔티티를 데이터베이스에 삽입합니다. (참고: 함수를 원하는 대로 호출할 수 있습니다.)
@Insert
fun insert(night: SleepNight)
@Update
fun update(night: SleepNight)
나머지 기능에는 편의 주석이 없으므로 @Query 주석을 사용하고 SQLite 쿼리를 제공해야 합니다.
@Query
fun get(key: Long): SleepNight?
:키를 주목하십시오. 쿼리에서 콜론 표기법을 사용하여 함수의 인수를 참조합니다.
("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
@Delete 주석은 하나의 항목을 삭제하고 @Delete를 사용하고 삭제할 SleepNight 목록을 제공할 수 있습니다. 단점은 테이블에 있는 내용을 가져오거나 알아야 한다는 것입니다. @Delete 주석은 특정 항목을 삭제하는 데 유용하지만 테이블에서 모든 항목을 지우는 데는 효율적이지 않습니다.
@Query("DELETE FROM daily_sleep_quality_table")
fun clear()
데이터베이스에서 "오늘밤"을 가져오려면 nightId로 정렬된 결과 목록의 첫 번째 요소를 내림차순으로 반환하는 SQLite 쿼리를 작성하십시오. 하나의 요소만 반환하려면 LIMIT 1을 사용합니다.
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
이번에는 이전에 생성한 Entity 및 DAO를 사용하는 Room 데이터베이스를 생성합니다.
@Database 주석이 달린 추상 데이터베이스 홀더 클래스를 생성해야 합니다. 이 클래스에는 데이터베이스가 존재하지 않는 경우 데이터베이스 인스턴스를 생성하거나 기존 데이터베이스에 대한 참조를 반환하는 메서드가 있습니다.
Room 데이터베이스를 가져오는 것은 약간 복잡하므로 코드를 시작하기 전의 일반적인 프로세스는 다음과 같습니다.
RoomDatabase를 확장하는 공용 추상 클래스를 만듭니다. 이 클래스는 데이터베이스 홀더 역할을 합니다. Room이 구현을 생성하기 때문에 클래스는 추상입니다.
@Database로 클래스에 주석을 답니다. 인수에서 데이터베이스의 엔터티를 선언하고 버전 번호를 설정합니다.
컴패니언 개체 내에서 SleepDatabaseDao를 반환하는 추상 메서드 또는 속성을 정의합니다. Room은 당신을 위해 body를 생성할 것입니다.
전체 앱에 대해 하나의 Room 데이터베이스 인스턴스만 필요하므로 RoomDatabase를 싱글톤으로 만드십시오.
Room의 데이터베이스 빌더를 사용하여 데이터베이스가 존재하지 않는 경우에만 데이터베이스를 생성하십시오. 그렇지 않으면 기존 데이터베이스를 반환합니다.
팁:
코드는 모든 Room 데이터베이스에 대해 거의 동일하므로 이 코드를 템플릿으로 사용할 수 있습니다.
@Database()
abstract class SleepDatabase : RoomDatabase() {}
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract val sleepDatabaseDao: SleepDatabaseDao
companion object {}
@Volatile로 INSTANCE에 주석을 답니다. 휘발성 변수의 값은 캐시되지 않으며 모든 쓰기 및 읽기는 주 메모리에서 수행됩니다. 이렇게 하면 INSTANCE 값이 항상 최신 상태이고 모든 실행 스레드에서 동일한지 확인하는 데 도움이 됩니다. 즉, 한 스레드에서 INSTANCE에 대해 변경한 내용을 즉시 다른 모든 스레드에서 볼 수 있으며 두 개의 스레드가 각각 캐시에서 동일한 엔터티를 업데이트하여 문제가 발생하는 상황이 발생하지 않습니다.
@Volatile
private var INSTANCE: SleepDatabase? = null
fun getInstance(context: Context): SleepDatabase {}
여러 스레드가 동시에 데이터베이스 인스턴스를 요청할 수 있으므로 하나가 아닌 두 개의 데이터베이스가 생성됩니다. 이 문제는 이 샘플 앱에서는 발생할 가능성이 없지만 더 복잡한 앱에서는 가능합니다. 데이터베이스를 동기화하기 위해 코드를 래핑한다는 것은 한 번에 하나의 실행 스레드만 이 코드 블록에 들어갈 수 있다는 것을 의미하며, 이는 데이터베이스가 한 번만 초기화되도록 합니다.
synchronized(this) {}
var instance = INSTANCE
return instance
if (instance == null) {}
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database")
Android Studio는 유형 불일치 오류를 생성합니다. 이 오류를 제거하려면 다음 단계에서 마이그레이션 전략과 build()를 추가해야 합니다.
일반적으로 스키마가 변경될 때 마이그레이션 전략을 마이그레이션 개체에 제공해야 합니다. 마이그레이션 개체는 데이터가 손실되지 않도록 이전 스키마의 모든 행을 새 스키마의 행으로 변환하는 방법을 정의하는 개체입니다. 마이그레이션은 이 코드랩의 범위를 벗어납니다. 간단한 해결책은 데이터베이스를 파괴하고 재구축하는 것입니다. 즉, 데이터가 손실됩니다.
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {
abstract val sleepDatabaseDao: SleepDatabaseDao
companion object {
@Volatile
private var INSTANCE: SleepDatabase? = null
fun getInstance(context: Context): SleepDatabase {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = Room.databaseBuilder(
context.applicationContext,
SleepDatabase::class.java,
"sleep_history_database"
)
.fallbackToDestructiveMigration()
.build()
INSTANCE = instance
}
return instance
}
}
}
}
이제 Room 데이터베이스 작업을 위한 모든 빌딩 블록이 있습니다. 이 코드는 컴파일되고 실행되지만 실제로 작동하는지 알 수 있는 방법이 없습니다. 따라서 몇 가지 기본 테스트를 추가해야합니다.
이 단계에서는 제공된 테스트를 실행하여 데이터베이스가 작동하는지 확인합니다. 이렇게 하면 데이터베이스를 구축하기 전에 데이터베이스가 작동하는지 확인하는 데 도움이 됩니다. 제공된 테스트는 기본입니다. 프로덕션 앱의 경우 모든 DAO에서 모든 기능과 쿼리를 실행합니다.
앱에는 androidTest 폴더가 있습니다. 이 androidTest 폴더에는 테스트에 Android 프레임워크가 필요하므로 실제 또는 가상 장치에서 테스트를 실행해야 한다고 말하는 멋진 방법인 Android 계측과 관련된 단위 테스트가 포함되어 있습니다. 물론 Android 프레임워크를 포함하지 않는 순수 단위 테스트를 만들고 실행할 수도 있습니다.
테스트 코드에 대한 간략한 설명입니다.
m1은 room라이브러리와 잘 호환이 안되므로 build.gradle(Module)에
kapt 'org.xerial:sqlite-jdbc:3.34.0'를 추가하면 정상적으로 작동합니다.
모든 테스트를 통과했으므로 이제 몇 가지 사항을 알게 되었습니다.
@Entity로 주석이 달린 데이터 클래스로 테이블을 정의하십시오. @ColumnInfo로 주석이 달린 속성을 테이블의 열로 정의합니다.
@Dao 주석이 달린 인터페이스로 DAO(Data Access Object)를 정의합니다. DAO는 Kotlin 함수를 데이터베이스 쿼리에 매핑합니다.
주석을 사용하여 @Insert, @Delete 및 @Update 함수를 정의합니다.
다른 쿼리에 대한 매개변수로 SQLite 쿼리 문자열과 함께 @Query 주석을 사용합니다.
데이터베이스를 반환하는 getInstance() 함수가 있는 추상 클래스를 만듭니다.
테스트를 사용하여 데이터베이스와 DAO가 예상대로 작동하는지 테스트하십시오. 제공된 테스트를 템플릿으로 사용할 수 있습니다.