[Kotlin] 7장. 앱 개발 - 데이터베이스

Hwichan Ji·2020년 12월 25일
0

Kotlin

목록 보기
7/11
post-thumbnail

이것이 안드로이드다 with 코틀린(고돈호 지음) 으로 공부한 내용을 정리한 글입니다.

데이터베이스

관계형 데이터베이스

안드로이드에서 사용하는 데이터베이스인 SQLite는 관계형 데이터베이스입니다. 관계형 데이터베이스는 데이터의 저장 형태와 관계를 정의하는 데이터베이스입니다.

테이블과 쿼리

테이블

테이블은 한 종류의 데이터들이 저장되는 단위입니다. 데이터의 속성은 column으로 구분하며, 각 column에 값이 채워진 한 줄의 데이터 단위를 row라고 합니다.

쿼리

관계형 데이터베이스는 SQL이라는 데이터를 정의, 조작, 제어하는 언어를 사용합니다. 그리고 SQL의 명령어를 쿼리라고 합니다.

SELECT 속성
FROM 테이블
WHERE 조건

쿼리의 종류
1. DDL(Data Definition Language): 데이터의 구조를 정의하는 명령어
2. DML(Data Manipulation Language): 데이터를 조작하는 명령어
3. DCL(Data Control Language): 데이터베이스 권한과 관련된 명령어

SQLite 데이터베이스

SQLite는 안드로이드의 기본 데이터베이스이자 경량화된 데이터베이스입니다.

데이터 구조 설계(테이블 생성)

DDL문을 사용하여 테이블을 생성합니다

CREATE TABLE 테이블명 (
    [속성1] [타입] [옵션], [속성2] [타입], ...
)

속성의 타입

  • INTEGER: 정수형 속성
  • TEXT: 문자형 속성
  • REAL: 실수형 속성
  • BLOB, NUMERIC 등
CREATE TABLE memo (
    no INTEGER PRIMARY KEY,
    content TEXT,
    datetime INTEGER
)

PRIMARY KEY로 설정된 속성이 INTEGER 타입일 때, 데이터가 추가되면 자동으로 이전 데이터에서 1 증가한 수로 설정됩니다.

SQLite Open Helper

SQLite를 사용하기 위해서는 ContextcreateDatabase() 메서드를 사용하거나, SQLiteOpenHelper라는 클래스를 상속받아서 사용합니다.

SQLiteOpenHelper 클래스는 데이터베이스를 파일로 생성하고 코틀린 코드에서 사용할 수 있도록 데이터베이스와 연결하는 역할을 합니다.

class SqliteHelper(context: Context, name: Sring, version:Int) :
        SQLiteOpenHelper(context, name, null, version) {
    
    override fun onCreate(db: SQLiteDatabase?) {
        val create = "create table memo (" +
                "no interger primary key, " +
                "content text, " +
                "datetime integer" +
                +)"
    }
    
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
    
    }
}

추가로 SQLite를 통해 INSERT, SELECT, UPDATE, DELETE할 때 필요한 데이터 클래스도 정의해야 합니다.

data class Memo(var no: Long?, var content: String, var datetime: Long)

숫자 범위의 차이로 인해 데이터베이스에서 INTEGER로 정의한 속성은 데이터 클래스에서 Long으로 정의해야 합니다.

삽입 메서드

SQLiteOpenHelper를 이용하여 데이터를 삽일할 때는 키와 값의 쌍 형태로 사용되는 ContentValues 클래스를 이용합니다.

fun insertMemo(memo: Memo) {
    val values = ContentValues()
    values.put("content", memo.content)
    values.put("datetime", memo.datetime)
    
    val wd = writeableDatabase
    wd.insert("memo", null, values)
    wd.close()
}

조회 메서드

fun selectMemo(): MutableList<Memo> {
    val list = mutableListOf<Memo>()
    
    val select = "select * from memo"
    val rd = readableDatabase
    val cursor = rd.rawQuery(select, null)
    
    while(cursor.moveToNext()) {
        val no = cursor.getLong(cursor.getClumnIndex("no"))
        val content = cursor.getString(cursor.getColumnIndex("content")
        val datetime = cursor.getLong(cursor.getColumnIndex("datetime")
        
        list.add(Memo(no, content, datetime))
    }
    
    cursor.close()
    rd.close()
    
    return list
}

수정 메서드

PRIMARY KEY를 통해 데이터베이스 상의 위치를 지정하고 데이터를 수정합니다.

fun updateMemo(memo: Memo) {
    val values = ContentValues()
    values.put("content", memo.content)
    values.put("datetime", memo.datetime)
    
    val wd = writableDatabase
    wd.update("memo", values, "no = ${memo.no}", null)
    wd.close()
}

삭제 메서드

fun deleteMemo(memo: Memo) {
    val delete = "delete from memo where no = ${memo.no}"
    
    val db = writableDatabase
    db.execSQL(delete)
    db.close()
}

Room: ORM 라이브러리

ORM(Object-Relational Mapping)은 객체와 관계형 데이터베이스의 데이터를 매핑하고 변환하는 기술이며 안드로이드는 SQLite를 코드 관점에서 접근할 수 있도록 ORM 라이브러리인 Room을 제공합니다.

Room 추가하기

Room은 어노테이션을 사용하기 때문에 Annotaiton Processing를 위한 kapt 플러그인을 추가해야 합니다.

apply plugin: 'kotlin-kapt'
// ...
def room_version = "2.2.0 "
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"

어노테이션
@명령어 형태를 가지며, 주석 형태의 문자열을 실제 코드로 생성해주는 것을 말함. 컴파일 시에 코드로 생성됨

Room 클래스 정의

@Entity 어노테이션이 적용된 클래스는 테이블로 변환됩니다.

@Entity(tableName = "orm_memo")
class RoomMemo {
    @PrimaryKey(autoGenerate=true)
    @ColumnInfo
    var no: Long? = null
    
    @Ignore
    var temp: String = "임시로 사용되는 데이터"
}

@ColumnInfo 어노테이션이 적용된 변수는 테이블의 속성으로 변환되며 추가로 @PrimaryKey 어노테이션을 적용하면 테이블의 primary key로 사용됩니다. @Ignore 어노테이션은 테이블과 관계 없는 변수라는 것을 명시하는 것입니다.

RoomMemoDAO 인터페이스 정의

Room은 데이터베이스에 읽고 쓰는 메서드를 인터페이스 형태로 설계하고 사용합니다.

DAO(Data Access Object)
데이터베이스에 접근해서 DML 쿼리를 실행하는 메서드의 모음

@Dao 어노테이션을 통해 DAO 인터페이스임을 명시합니다.

@Dao
interface RoomMemoDao {
    @Query("select * from orm_memo")
    fun getAll(): List<RoomMemo>
    
    @Insert(onConflict = REPLACE)
    fun insert(memo: RoomMemo)
    
    @Delete
    fun delete(memo: RoomMemo)
}

Room은 다른 ORM 툴과는 다르게 SELECT 쿼리를 직접 작성하도록 설계되어 있습니다. 따라서 @Query 어노테이션을 사용한 뒤 쿼리를 작성해야 합니다.

@Insert 어노테이션에 onConflict = REPLACE 옵션이 설정되고 삽입한 데이터와 동일한 키를 가진 데이터가 이미 있는 경우 UPDATE 쿼리로 실행됩니다.

AppDatabase 클래스 정의

SQLiteOpenHelper처럼 Room도 RoomDatabase 클래스가 존재합니다. 다만 SQLiteOpenHelper와 달리 추상 클래스로 생성해야 합니다.

@Database(entities = arrayOf(RoomMemo::class), version = 1, exportSchema = false)
abstract class RoomHelper : RoomDatabase() {
    // Dao 인터페이스의 구현체를 사용할 수 있는 메서드명 정의
    abstact fun roomMemoDao(): RoomMemoDao 
}

@Database 어노테이션을 작성하여 여러 속성을 적용합니다. entities는 Room이 사용할 테이블 클래스 목록, version은 데이터베이스의 버전, exportSchema는 스키마 정보를 파일로 출력할지 여부를 설정합니다.

profile
안드로이드 개발자를 꿈꾸는 사람

0개의 댓글