Room 데이터베이스란?
✔ 복잡한 쿼리를 잘 몰라도 SQLite를 코드 관점에서 접근할 수 있는 ORM 라이브러리
Room 추가하기
build.gradle
Room 버전
https://developer.android.com/jetpack/androidx/releases/room
Room | Android 개발자 | Android Developers
Room Room 지속성 라이브러리는 SQLite에 추상화 레이어를 제공하여 SQLite를 완벽히 활용하면서 더 견고한 데이터베이스 액세스를 가능하게 합니다. 최근 업데이트 현재 안정화 버전 다음 버전 후보
developer.android.com
RoomMemo 클래스 정의하기
RoomMemo.kt
✔ 데이터베이스의 테이블과 속성 값을 지정해주기 위한 클래스
@Entity(tableName = "room_memo")
class RoomMemo {
@PrimaryKey(autoGenerate = true)
// insert 시 no에 값이 없을 때 자동 증가된 숫자값 입력
@ColumnInfo
var no: Long? = null
@ColumnInfo
var content: String = ""
@ColumnInfo(name = "date") // 데이터베이스 속성 이름을 따로 지정
var datetime: Long = 0
// RoomMemo 생성하고 속성값을 넣어줄 때 편리하게 사용
constructor(content: String, datetime: Long) {
this.content = content
this.datetime = datetime
}
}
◽ Entity annotation을 사용함으로써 클래스 자체가 데이터베이스 테이블이 되도록 만든다.
◽ ColumnInfo annotation을 사용함으로써 선언할 변수값들을 데이터베이스의 컬럼(속성) 값이 되도록 만든다.
RoomMemoDao 인터페이스
✔ DAO란 Data Access Object의 약어로 데이터베이스에 접근해서 DML 쿼리를 실행하는 메서드의 모음이다.
✔ 데이터베이스에 읽고 쓰는 메서드를 인터페이스 형태로 설계하고 사용
✔ 코드 없이 이름만 명시하는 형태로 인터페이스를 만들면 Room이 나머지 코드를 자동 생성한다.
@Dao
interface RoomMemoDao {
@Query("select * from room_memo")
fun getAll(): List<RoomMemo>
@Insert(onConflict = REPLACE)
fun insert(memo: RoomMemo)
@Delete
fun delete(memo: RoomMemo)
}
◽ 다른 ORM들과는 다르게 조회를 하는 select 쿼리는 직접 작성하도록 설계되어 있다.
◽ @Insert(onConflict = REPLACE) 의 경우, 이미 존재하는 값에 대한 insert를 시도 시, update를 해주는 방식으로 변경한다는 뜻이다.
◽ annotation 표
RoomHelper.kt
✔ 테이블(RoomMemo)을 컨트롤할 수 있는 함수의 모음(RoomMemoDAO)을 Helper(RoomHelper)를 통해서 꺼내서 쓸 수 있도록 만들어주는 클래스
@Database(entities = arrayOf(RoomMemo::class), version = 1, exportSchema = false)
abstract class RoomHelper : RoomDatabase() {
abstract fun roomMemoDao(): RoomMemoDao
}
◽ 이렇게 빈 껍데기 코드만 작성해두는 것만으로도 Room 라이브러리를 통해서 미리 만들어져 있는 코드를 사용할 수 있다.
MainActivity.kt
class MainActivity : AppCompatActivity() {
val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }
lateinit var helper: RoomHelper
lateinit var memoAdapter: RecyclerAdapter
val memoList = mutableListOf<RoomMemo>()
lateinit var memoDAO: RoomMemoDAO
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(binding.root)
helper = Room.databaseBuilder(this, RoomHelper::class.java, "room_memo").build()
// RoomHelper에 있는 추상 메서드 roomMemoDao가 라이브러리에 의해 채워진다.
memoDAO = helper.roomMemoDao() // 초기화
memoAdapter = RecyclerAdapter(memoList) // 초기화
refreshAdapter()
with (binding) {
recyclerMemo.adapter = memoAdapter // 리싸이클러뷰에 어댑터 연결
recyclerMemo.layoutManager = LinearLayoutManager(this@MainActivity)
buttonSave.setOnClickListener {
val content = editMemo.text.toString()
if (content.isNotEmpty()) {
val memo = RoomMemo(content, System.currentTimeMillis())
editMemo.setText("") // 텍스트 입력창 초기화
insertMemo(memo)
}
}
}
}
fun insertMemo(memo: RoomMemo) {
CoroutineScope(Dispatchers.IO).launch {
memoDAO.insert(memo)
refreshAdapter()
}
}
// 비동기 방식으로 데이터베이스에서 리스트 불러오고 갱신하기
fun refreshAdapter() {
CoroutineScope(Dispatchers.IO).launch {
memoList.clear()
memoList.addAll(memoDAO.getAll())
withContext(Dispatchers.Main) {
memoAdapter.notifyDataSetChanged()
}
}
}
}
◽ Room 데이터베이스의 CRUD를 실행하기 위해서는 비동기식 처리가 필요하다. databaseBuilder의 allowMainThreadQueries 메서드를 통해서 메인 스레드에서 처리할 수도 있지만, 실제 개발에서 사용되지는 않는 방법이다.
RecyclerAdapter.kt
class RecyclerAdapter(val listData: MutableList<RoomMemo>) : RecyclerView.Adapter<RecyclerAdapter.Holder>() {
var helper:RoomHelper? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val binding = ItemRecyclerBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return Holder(binding)
}
override fun getItemCount(): Int {
return listData.size
}
override fun onBindViewHolder(holder: Holder, position: Int) {
val memo = listData.get(position)
holder.setRoomMemo(memo)
}
inner class Holder(val binding: ItemRecyclerBinding) : RecyclerView.ViewHolder(binding.root) {
var mRoomMemo:RoomMemo? = null
init {
binding.buttonDelete.setOnClickListener {
helper?.roomMemoDao()?.delete(mRoomMemo!!)
listData.remove(mRoomMemo)
notifyDataSetChanged()
}
}
fun setRoomMemo(memo:RoomMemo) {
binding.textNo.text = "${memo.no}"
binding.textContent.text = memo.content
val sdf = SimpleDateFormat("yyyy/MM/dd hh:mm")
binding.textDatetime.text = "${sdf.format(memo.datetime)}"
this.mRoomMemo = memo
}
}
}
activity_main.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerMemo"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="@+id/editMemo"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editMemo"
android:layout_width="0dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:ems="10"
android:hint="메모를 입력하세요"
android:inputType="textMultiLine"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/buttonSave"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/buttonSave"
android:layout_width="wrap_content"
android:layout_height="100dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
android:text="저장"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
cs |
item_recycler.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/textNo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="32dp"
android:text="01"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textContent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="32dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:text="메모 내용 표시"
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/buttonDelete"
app:layout_constraintStart_toEndOf="@+id/textNo"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textDatetime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:text="2020/01/01 13:57"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textContent" />
<Button
android:id="@+id/buttonDelete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="32dp"
android:text="삭제"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
|
cs |
'Android > Concept' 카테고리의 다른 글
[Android] [Kotlin] 코루틴 (0) | 2021.09.21 |
---|---|
[Android] [Kotlin] 스레드와 루퍼 (0) | 2021.09.20 |
[Android] [Kotlin] SharedPreferences (0) | 2021.09.19 |
[Android] [Kotlin] SQLite (0) | 2021.09.14 |
[Android] [Kotlin] ViewPager (뷰페이저) (0) | 2021.09.13 |