개발 공부/안드로이드

Android Room 2 - 적용하기

yong_DD 2022. 7. 3. 15:57

Room을 적용하여 대학교의 학생 목록을 관리하는 예시를 만들어보았다.

완성본

[구현 사항]

  • db의 필요사항 - Primary key로 있을 id 값, 이름, 전공
  • 삭제/추가 구현
  • vararg 테스트를 위한 리스트 추가
  • id 값은 자동으로 계산해서 추가(+1씩)

* viewBinding 사용, 코틀린으로 작성되었습니다.

enum class Major(val korName:String) {
    ECONOMICS("경제학과"),
    BIOLOGY("생물학과"),
    LAWS("법학과"),
    ACCOUNTING("회계학과"),
    HISTORY("역사학과")
}

1. Entitiy

@Entity(tableName = "student")
data class Student(
    var name : String? = null,
    var major : String? = null
){
    @PrimaryKey(autoGenerate = true) var id :Int? = null
}

목표가 autoIncreate가 되는 id 값이였기 때문에 autoGenerate를 true값으로 주고,

이 부분은 작성할 필요가 없어서 매서드 부분에서 제외시켰다.

 

2. DAO

@Dao
interface StudentDao {
    @Insert
    fun insertStudent(student: Student)

    @Insert
    fun insertAll(vararg student: Student)

    @Delete
    fun deleteStudent(student: Student)

    @Query("SELECT * FROM student")
    fun getAll() : List<Student>
}

학생 한 명을 넣을 수 있는 intsertStudio 와 여러 리스트를 넣기 위해 vararg를 사용한 insertAll을 만들었고, 목록을 가져오고 한 명씩 지울 수 있도록 추가했다.

 

3. RoomDatabase

@Database(entities = [Student::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun studentDao():StudentDao

    companion object {
        @Volatile
        private var instance: AppDatabase? = null

        fun getInstance(context: Context): AppDataase {
            synchronized(this) {
                var instance = instance

                if (instance == null) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        AppDatabase::class.java,
                        "student.db"
                    )
                        .fallbackToDestructiveMigration()
                        .build()
                    this.instance = instance
                }
                return instance
            }
        }
    }
}

 

4. 해당 Activity (RoomActivity)

class RoomActivity: AppCompatActivity() {

    private lateinit var binding : ActivityRoomBinding
    lateinit var db : AppDatabase
    private var selectMajor : Major? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityRoomBinding.inflate(layoutInflater)
        setContentView(binding.root)

        db = AppDatabase.getInstance(this)

        setSpinner()
        clickControl()
        getList()
    }

    private fun setSpinner(){
        val majorList =  Major.values().toMutableList()
        val majorNameList = mutableListOf<String>()
        majorList.forEach {
            majorNameList.add(it.korName)
        }
        val adapter = ArrayAdapter<String>(
            this,android.R.layout.simple_spinner_dropdown_item,majorNameList
        )
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        binding.majorSpinner.adapter = adapter
        binding.majorSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                selectMajor = majorList[position]
            }
            override fun onNothingSelected(parent: AdapterView<*>?) {}
        }

        selectMajor = majorList[0]
    }

    private fun clickControl(){
        binding.addBtn.setOnClickListener {
            if(binding.nameText.text.isNullOrBlank()) return@setOnClickListener
            addStudent()
        }

        binding.allAddButton.setOnClickListener {
            addList()
        }
    }

    private fun getList(){
        Thread(Runnable{
            runOnUiThread {
                binding.listView.removeAllViews()
            }
            val inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
            db.studentDao().getAll().forEach { student ->
                runOnUiThread {
                    val studentView = ItemRoomBinding.inflate(inflater,binding.listView,false)
                    studentView.idTextView.text = student.id.toString()
                    studentView.nameTextView.text = student.name
                    studentView.majorTextView.text = student.major
                    studentView.deleteBtn.setOnClickListener {
                        Log.d("clickBtn","click deleteBtn")
                        deleteStudent(student)
                    }
                    binding.listView.addView(studentView.root)
                }
            }
        }).start()

    }

    private fun addList(){
        val list = mutableListOf<Student>()
        list.apply {
            add(Student("김나나",Major.ACCOUNTING.korName))
            add(Student("이상엽",Major.BIOLOGY.korName))
            add(Student("박하나",Major.ECONOMICS.korName))
            add(Student("정이든",Major.HISTORY.korName))
            add(Student("서율",Major.LAWS.korName))
            add(Student("정재영",Major.HISTORY.korName))
            add(Student("김철수",Major.ACCOUNTING.korName))
            add(Student("박영희",Major.ECONOMICS.korName))
            add(Student("조유나",Major.HISTORY.korName))
            add(Student("박윤철",Major.LAWS.korName))
        }

        Thread(Runnable {
            db.studentDao().insertAll(*list.toTypedArray())
            getList()
        }).start()
    }

    private fun addStudent(){
        val name = binding.nameText.text.toString()
        Thread(Runnable {
            db.studentDao().insertStudent(Student(name,selectMajor!!.korName))
            getList()
        }).start()
    }

    private fun deleteStudent(student: Student){
        Thread(Runnable {
            db.studentDao().deleteStudent(student)
            getList()
        }).start()
    }
}
  • 전체 추가에는 임의의 리스트를 만들어서 insertAll을 사용해서 insert하도록 하였다
  • 이름을 작성하는 란과, 만들어 놓았던 Major enum class를 통해 spinner로 선택하도록 해서 한 개씩 추가하도록 해놓았다.

[실제 실행된 화면]

 
 
  • 전체추가를 2번 누른 상태로 id 값이 자동으로 들어가서 값이 다르기 때문에 나머지 값이 같아도 잘 들어갔다.

 

[시행 착오]

  • 초반에 Entity에 enum class인 Major을 넣어다가 당연히 값이 제대로 오지 않았고 korName값을 이용해서 넣기에 오류도 안나고 꺼내도 바로 리스트에 보이도록 하였다.

좀 더 발전하면 Major table을 만들어서 Primary 값을 통해 가져오면 될 것 같다.

  • Entity Cannot find setter for field 에러
@Entity
data class Student(
    @PrimarKey val id :Int,
    val name : String,
    val major : String
)

처음에 위와 같이 작성을 했다가 Cannot find setter for field 에러가 발생했다.

검색을 해보니 기본 설정값을 넣어줘야 한다고 해서 넣었더니 오류가 없어졌다.

 

@Entity(tableName = "student")
data class Student(
    @PrimaryKey var id :Int? = null,
    var name : String? = null,
    var major : String? = null
)

차후는 필요에 의해 상단의 최종 버전으로 발전되었다!