[Android] UMC Study - 7주차
1. DMBS (Database Management System)
다양한 형태의 데이터베이스를 조작하는 별도의 소프트웨어!
- 정의 : 데이터에 대한 형식, 구조, 제약조건들을 명세하는 기능이다. 데이터베이스에 대한 정의 및 설명은 카탈로그나 사전의 형태로 저장된다.
- 구축 : DBMS가 관리하는 기억 장치에 데이터를 저장하는 기능이다.
- 조작 : 특정한 데이터를 검색하기 위한 질의, 데이터베이스의 갱신, 보고서 생성 기능 등을 포함한다.
- 공유 : 여러 사용자와 프로그램이 데이터베이스에 동시에 접근하도록 하는 기능이다.
- 보호 : 하드웨어나 소프트웨어의 오동작 또는 권한이 없는 악의적인 접근으로부터 시스템을 보호한다.
- 유지보수 : 시간이 지남에 따라 변화하는 요구사항을 반영할 수 있도록 하는 기능이다.
RDBMS
Relational Data Base 의 약자로써 관계형 데이터베이스를 관리하는 시스템!
관계형 데이터베이스란 테이블(table)로 이루어져 있으며, 이 테이블은 키(key)와 값(value)의 관계를 나타냄.
이처럼 데이터의 종속성을 관계(relationship)로 표현하는 것이 관계형 데이터베이스의 특징!
2. RoomDB
- 데이터를 저장하는 방법
서버에 연결을 하여 서버 데이터베이스에 저장을 하는 방법
vs
(기기)내장 데이터베이스에 저장하는 방법
후자의 경우 데이터의 규모나 처리 가능한 양은 적지만 속도가 매우 빠르다는 장점이 있음!
안드로이드에서 효과적인 데이터 관리를 위하여 구조화된 내부 SQL Database인 SQLite Database를 지원하고 있으며, 이를 다루기 위한 라이브러리가 Room 이다.
step 1. Dependency 추가
// RoomDB
implementation 'androidx.room:room-runtime:2.3.0'
implementation 'androidx.room:room-ktx:2.3.0'
kapt 'androidx.room:room-compiler:2.3.0'
step 2. Entity 구현
Entity란? 관련이 있는 속성들이 모여 하나의 정보 단위를 이룬 것을 의미
관계형 데이터베이스의 테이블이 Entity를 통해 만들어지는것임!
-
@(어노테이션)으로 테이블명, 컬럼명, primary key 등 지정 가능
-
SQLite에서는 대소문자를 구분하지 않음
-
PrimaryKey(주키)가 하나 필수로 들어가야 하며 autoGenerate = true 로 설정해주었을 시 autoIncrement설정이 되어 1부터 시작하여 자동으로 1씩 증가하는 Integer값이 할당 되는 것임
@Entity(tableName = "SongTable")
data class Song(
@ColumnInfo("musicTitle") var title : String = "",
var artist : String = "",
var albumTitle : String = "",
@PrimaryKey val music : String = "",
var playTime : Int = 0,
var currentMillis : Int = 0,
var isLike : Boolean = false,
val coverImg : Int = R.drawable.img_album_exp2
@Ignore val dummyData : Int = 0
) {
//
// @PrimaryKey(autoGenerate = true) var id : Int = 0
}
step 3. Dao 구현
@Query/@Insert/@Delete/@Update 들을 정의하여 해당 메소드의 동작을 지정해 줘야함.
@Dao
interface SongDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(song:Song)
@Update
fun update(song:Song)
@Delete
fun delete(song:Song)
@Query("SELECT * FROM SongTable")
fun getSongs() : List<Song>
@Query("SELECT * FROM SongTable WHERE music = :music")
fun getSong(music : String) : Song
@Query("UPDATE SongTable SET isLike = :isLike WHERE music = :music")
fun updateIsLikeByMusic(isLike : Boolean, music : String)
@Query("SELECT * FROM SongTable WHERE isLike = :isLike")
fun getIsLikedSongs(isLike : Boolean) : List<Song>
}
@Insert의 경우에는 뒤에 onConflict 를 정의해줄 수 있는데, Primary가 겹치는 일을 방지할 수 있음!
step 4. Room Database 생성
```kotlin
@Database(entities = [Song::class], version = 1)
abstract class SongDB : RoomDatabase() {
abstract fun SongDao() : SongDao
companion object{
private var instance : SongDB? = null
@Synchronized
fun getInstance(context: Context) : SongDB? {
if (instance == null){
synchronized(SongDB::class){
instance = Room.databaseBuilder(
context.applicationContext,
SongDB::class.java,
"user-database"
).fallbackToDestructiveMigration().build()
}
}
return instance
}
}
}
-
먼저 RoomDatabase()클래스를 상속받는 추상클래스를 정의해 준 뒤, @Database 어노테이션으로 데이터베이스임을 표시해 줘야함.
-
어노테이션의 괄호 안에는 위에서 정의해 주었던 Entity(테이블)들과 version이 들어감
앱을 계속 개발하고 업데이트를 진행하다보면 entity의 구조가 변경될 일이 생기는데, 이 때 이전 구조와 현재 구조를 구분해 주는 역할을 하는 것이 version이기 때문에 데이터베이스 구조가 바뀌고, 이전의 데이터들을 보존해야 하는 상황이라면 버전을 꼭 올려주어야 함
그렇지 않다면 다음과 같은 오류를 마주함...
Looks like you've changed schema but forgot to update the version number. You can simply fix this by increasing the version number.
객체를 생성할 때 databaseBuilder라는 static 메서드를 사용하는데
context와, database 클래스 그리고 데이터 베이스를 저장할 때 사용할 데이터베이스의 이름을 정해서 넘겨주면 됨!
이때 마지막으로 넘겨준 데이터베이스의 이름이 다른 데이터베이스랑 겹치면 꼬여버리니 주의해야 한다.
데이터베이스를 빌드하는 과정은 매우 무겁고 자원소모가 크기 때문에 메인스레드에서 작업하는 것을 기본적으로 허용하진 않아서 따라서 스레드를 따로 생성하여 데이터베이스에 접근해야 하지만, 굳이 메인스레드에서 작업을 해야 한다면
Room.databaseBuilder(context, DBclass, name).allowMainThreadQueries().build()
를 통해서 허용해줄 수 있다.
안드로이드 공식문서에서는 다음과 같이 데이터베이스 객체를 인스턴스화 할 때 에는 리소스를 많이 소비하기 때문에 싱글톤 패턴을 사용할 것을 권장하고 있음.
싱글톤(Singleton) 패턴이란?
애플리케이션이 시작될 때 어떤 클래스가 최초 한번만 메모리를 할당하고 이후 똑같은 객체를 여러번 생성하지 않고 최초 생성된 하나의 객체만 계속해서 사용하는 디자인 패턴
참고 블로그 주소 : https://bacassf.tistory.com/59
코틀린의 경우 object로 클래스를 정의하면 바로 싱글톤 패턴이 되며, companion object를 사용해서 자바처럼 구현 가능
위의 데이터베이스는 companion object 내에 private으로 instance를 생성하여 외부에서 접근할 수 없게 하였습니다.
그 다음에 외부에서 접근할 때에는 getInstance함수를 사용하여 companion object내부에서 생성된 instance에 접근하는 방식으로 구현한 것
이렇게 한다면 외부에서 객체를 새로 생성하지 않고도 최초 한번 생성한 companion object 내의 instance만 사용하게 될것이다!
Author And Source
이 문제에 관하여([Android] UMC Study - 7주차), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sjlim/Android-UMC-Study-7주차저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)