코딩하는 일용직 노동자

Room 사용법을 ㅇrㄹr보ㅈr 본문

안드로이드

Room 사용법을 ㅇrㄹr보ㅈr

bacass 2020. 8. 17. 14:21

기존 안드로이드 로컬 데이타베이스를 사용할땐 SQLite를 이용했습니다.
하지만 사용방법이 매우 불편하기 때문에 ORMLite 나 greenDAO 같은 별도의 ORM(Object-relational mapping)을 이용하거나 Realm을 이용합니다.

Room은 SQLite를 편리하게 사용하도록 구글이 공식적으로 내놓은 ORM이라고 생각하시면 됩니다.
(왜 이걸 이제야 내놓는지... 만들어줄거면 진작에 나왔어야 하는거 아닌가 싶으ㄴ... -_-;;)

이번 포스팅에선 Room을 어떻게 사용하는지 기초적인 방법을 확인해보겠습니다.


#1. Room의 기본구성요소
Entity : 데이타베이스의 테이블 이라고 생각하면 됩니다.
DAO(Data Access Object) : 데이타베이스를 조작하는 함수들을 여기서 interface로 정의해줍니다.
Database : 데이타베이스의 진입점입니다. DAO를 반환하는 추상메소드와 사용할 Entity목록을 작성해야 합니다.

Room 의 주요 구성요소
Room 아키텍쳐 다이어그램

#2. gradle 
app/build.gradle 에 Room을 사용하기 위한 등록을 합니다.

apply plugin: 'kotlin-kapt'
...

dependencies {
	// ROOM 데이타베이스
	def room_version = "2.2.5"

	implementation "androidx.room:room-runtime:$room_version"
	kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
}

 

개발환경이 Java인 경우에는 annotationProcessor를, Kotlin인 경우에는 kapt를 사용합니다.


#3. 테이블 만들기
이제 본격적으로 Room을 이용해 데이타베이스를 만들어보도록 하겠습니다.
@Entity 어노테이션을 추가해줍니다.
테이블을 만들기 위해 Room의 구성요소인 Entity를 만듭니다.

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

/**
 * 테이블명을 따로 지정해줄수 있다.
 * 만약 지정하지 않는다면 클래스명을 따를것이다.
 */
@Entity(tableName = "user")
data class User(
    /**
     * 모든 Entity는 PrivaryKey를 하나씩 포함해야 한다.
     * autoGenerate 옵션을 추가해서 id를 지정하지 않더라고 자동으로 생성하도록 해준다.
     */
    @PrimaryKey(autoGenerate = true)
    val id: Int?,

    /**
     * ColumnInfo는 테이블 내의 스키마를 따로 지정해준다.
     */
    @ColumnInfo(name = "name")
    val userName: String?,
    val age: Int?
)

 

#4. DAO 만들기
사용할 테이블을 만들었으니 이제 테이블에 접근할 함수들을 만들어줍니다.
@Dao 어노테이션을 추가해줍니다.

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy.REPLACE
import androidx.room.Query

@Dao
interface UserDao {
    @Query("SELECT * FROM user")
    fun getAll(): List<User>

    @Query("SELECT * FROM user WHERE id IN (:userIds)")
    fun loadAllByIds(userIds: IntArray): List<User>

    @Query("SELECT * FROM user WHERE name LIKE :name AND age LIKE :age LIMIT 1")
    fun findByNameAndAge(name: String, age: Int): User

    /**
     * onConflict = REPLACE 만약 같은 내용이 이미 있다면 덮어쓴다는 옵션이다.
     */
    @Insert(onConflict = REPLACE)
    fun insertUser(user: User)

    @Delete
    fun deleteUser(user: User)
}

 

#5. 데이타베이스 만들기
테이블과 DAO를 만들었으니, 이제 데이타베이스를 완성해줍니다.
@Database 어노테이션을 추가해줍니다.
RoomDatabase 를 상속받는 추상클래스로 만듭니다.
entities 에는 사용할 테이블을 지정해줄 수 있습니다. 여러개를 등록할 수 있지만 여기선 User Entity 하나만 등록하겠습니다.
versison 은 버전값입니다. 만약 테이블의 구조가 수정되었다면 이 값을 올려줘야 합니다.
데이타베이스는 싱글턴으로 관리되도록 만들어주겠습니다.

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

@Database(entities = arrayOf(User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {
    /**
     * UserDao를 반환하는 추상화 메소
     */
    abstract fun userDao(): UserDao

    companion object {
        private val DB_NAME = "user-room-db"

        /**
         * 데이타베이스를 만드는 작업은 리소스를 많이 필요로 한다.
         * 때문에 이 프로젝트 내에서 딱 하나만 만들어서 관리하는 것이 유리하다.
         * 싱글턴으로 만들도록 한다.
         */
        var instance: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase? {
            if (instance == null) {
                synchronized(AppDatabase::class) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        DB_NAME
                    )
//                        .fallbackToDestructiveMigration() // Entity의 구조가 바뀌거나 버전이 올라갔을때 기존 내용을 드랍하고 새로 작성하는 옵션.
                        .build()
                }
            }

            return instance
        }
    }
}

 

#6. 테스트 & 결과
AppDatabase 객체 db와 검색 결과를 저장할 User 리스트 객체를 만들고
DAO에 만들어둔 함수들을 호출해서 결과를 확인해보겠습니다.

class MainActivity : AppCompatActivity() {

    lateinit var db: AppDatabase
    var userList: List<User> = listOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        db = AppDatabase.getInstance(this)!!

        GlobalScope.launch {    // 1
            insertDB()
            sleep(1000)
            findByNameAndAge()
            sleep(1000)
            loadAllByIds()
            sleep(1000)
            deleteUser()
        }
    }

    private fun insertDB() {
        GlobalScope.launch {    // 1
            db.userDao().insertUser(User(null, userName = "홍길동", age = 24))
            db.userDao().insertUser(User(null, userName = "성춘향", age = 16))
            db.userDao().insertUser(User(null, userName = "이몽룡", age = 18))
            db.userDao().insertUser(User(null, userName = "소방자", age = 21))
            db.userDao().insertUser(User(null, userName = "최향단", age = 17))

            userList = db.userDao().getAll()
            for (user in userList) {
                Log.d("Room", "${user.id} / ${user.userName} / ${user.age}")
            }
            Log.d("Room", "=======================================")
            Log.d("Room", "  ")
        }
    }

    private fun findByNameAndAge() {
        GlobalScope.launch {    // 1
            var user = db.userDao().findByNameAndAge(name = "성춘향", age = 16)
            Log.d("Room", "${user.id} / ${user.userName} / ${user.age}")
        }
        Log.d("Room", "=======================================")
        Log.d("Room", "  ")
    }

    private fun loadAllByIds() {
        GlobalScope.launch {    // 1
            userList = db.userDao().loadAllByIds(intArrayOf(2, 3, 4))
            for (user in userList) {
                Log.d("Room", "${user.id} / ${user.userName} / ${user.age}")
            }
        }
        Log.d("Room", "=======================================")
        Log.d("Room", "  ")
    }

    private fun deleteUser() {
        GlobalScope.launch {    // 1
            db.userDao().deleteUser(User(null, userName = "소방자", age = 21))

            userList = db.userDao().getAll()
            for (user in userList) {
                Log.d("Room", "${user.id} / ${user.userName} / ${user.age}")
            }
        }
        Log.d("Room", "=======================================")
        Log.d("Room", "  ")
    }
}

insert, select, delete 가 모두 잘 된다.

github에 프로젝트 파일을 올려놨습니다.

https://github.com/Bacass/MyRoomExam

 

Bacass/MyRoomExam

Android Room Database Exam. Contribute to Bacass/MyRoomExam development by creating an account on GitHub.

github.com