a-5-1-2(3).Realm DBのデータ保存と更新、削除(配列以外、配列)

### a-5-1-2(3).Realm DBのデータ保存と更新、削除(配列以外、配列)

#### 目標設定
[一覧に戻る](https://qiita.com/ryouta33/items/57f5e51a26086151a0da)

#### Github

https://github.com/wakizaka24/AndroidTest

#### テスト実装

```kotlin:RealmTestActivity.kt
package com.example.androidtest

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import com.example.androidtest.databinding.ActivityRealmTestBinding
import io.realm.kotlin.ext.query
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlin.math.log

class RealmTestActivity : AppCompatActivity() {
    private lateinit var binding: ActivityRealmTestBinding

    @OptIn(DelicateCoroutinesApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityRealmTestBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)

        val dogResult = RealmTestService.realm.query<Dog>(query = "dogId == $0", args = arrayOf(2))
            .find()
        var dogName = "いない"
        if (dogResult.size > 0) {
            dogName = dogResult.first().name
        }

        val personNum = RealmTestService.realm.query<Person>().find().size
        Log.d("Realm情報", "抽出したDog名: $dogName 初期のPersonの数: $personNum")
        logOfRealm()
        // showMessage("抽出したDog名: $dogName 初期のPersonの数: $personNum")

        binding.addPerson.stateListAnimator = null
        binding.addPerson.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = this.query<Person>(
                    query = "personId == $0", args = arrayOf(2)).find()
                if (personResult.size == 0) {
                    copyToRealm(Person().apply {
                        personId = 2
                        name = "追加した人"
                        age = 30
                    })
                }
            }
            logOfRealm()
        }

        binding.updatePerson.stateListAnimator = null
        binding.updatePerson.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = this.query<Person>(
                    query = "personId == $0", args = arrayOf(2)).find()
                if (personResult.size > 0) {
                    personResult.first().name = "更新した人"
                }
            }
            logOfRealm()
        }

        binding.deletePerson.stateListAnimator = null
        binding.deletePerson.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = this.query<Person>(
                    query = "personId == $0", args = arrayOf(2)).find()
                if (personResult.size > 0) {
                    delete(personResult.first())
                }
            }
            logOfRealm()
        }

        binding.addPersonTask.stateListAnimator = null
        binding.addPersonTask.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = this.query<Person>(
                    query = "personId == $0", args = arrayOf(1)).find()
                var taskId = 0
                if (personResult.first().tasks.size > 0) {
                    val maxTask = personResult.first().tasks.maxBy { it.taskId!! }
                    taskId = maxTask.taskId!! + 1
                }
                personResult.first().tasks.add(Task().apply {
                    this.taskId = taskId
                    name = "追加したタスク"
                    finished = false
                })
            }
            logOfRealm()
        }

        binding.updatePersonTask.stateListAnimator = null
        binding.updatePersonTask.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = this.query<Person>(
                    query = "personId == $0", args = arrayOf(1)).find()
                if (personResult.size > 0 && personResult.first().tasks.size > 0) {
                    val minTask = personResult.first().tasks.minBy { it.taskId!! }
                    minTask.finished = true
                }
            }
            logOfRealm()
        }

        binding.deletePersonTask.stateListAnimator = null
        binding.deletePersonTask.setOnClickListener {
            RealmTestService.realm.writeBlocking {
                val personResult = query<Person>(
                    query = "personId == $0", args = arrayOf(1)).find()
                if (personResult.size > 0) {
                    val tasks = personResult.first().tasks.filter { it.finished }
                    personResult.first().tasks.removeAll(tasks)
                }
            }
            logOfRealm()
        }
    }

//    private fun showMessage(message: String) {
//        val builder = AlertDialog.Builder(this)
//        builder.setTitle("Realmの検証")
//            .setMessage(message)
//            .setPositiveButton("OK") { _, _ ->
//            }
//        builder.create()
//        builder.show()
//    }

    private fun logOfRealm() {
        val parsonResult = RealmTestService.realm.query<Person>().find()
        Log.d("Realm情報", "---Person---")
        for (i in 0 ..< parsonResult.size) {
            val parson = parsonResult[i]
            Log.d("Realm情報",
                "${parson.personId} ${parson.name} ${parson.age}")
            if (parson.tasks.size > 0) {
                Log.d("Realm情報", "---Person>Task---")
            }
            for (j in 0..<parson.tasks.size) {
                val task = parson.tasks[j]
                Log.d("Realm情報",
                    "${task.taskId} ${task.name} ${task.finished}")
            }
        }

        Log.d("Realm情報", "---Dog---")
        val dogResult = RealmTestService.realm.query<Dog>().find()
        for(i in 0 ..< dogResult.size) {
            val dog = dogResult[i]
            Log.d("Realm情報",
                "${dog.dogId} ${dog.name} ${dog.age}")
        }
    }
}
```

```xml:activity_realm_test.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginStart="8dp"
        android:layout_marginTop="4dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="4dp">

        <TextView
            android:id="@+id/text_1_text_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="0dp"
            android:text="Realmの検証"
            android:textSize="21sp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            tools:ignore="HardcodedText" />

        <Space
            android:layout_width="match_parent"
            android:layout_height="4dp" />

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            >

            <Button
                android:id="@+id/add_person"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personの追加(personId=2)"
                android:padding="0dp"
                android:layout_marginEnd="8dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />

            <Button
                android:id="@+id/update_person"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personの更新(personId=2, 名前を変更する)"
                android:padding="0dp"
                android:layout_marginEnd="8dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />

            <Button
                android:id="@+id/delete_person"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personの削除(personId=2)"
                android:padding="0dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />
        </LinearLayout>

        <Space
            android:layout_width="match_parent"
            android:layout_height="8dp" />

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="150dp"
            >

            <Button
                android:id="@+id/add_person_task"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personのtaskの追加(personId=1, タスクIDを最大+1)"
                android:padding="0dp"
                android:layout_marginEnd="8dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />

            <Button
                android:id="@+id/update_person_task"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personのtaskの更新(personId=1, タスクIDの最小のレコードをfinished=trueにする)"
                android:padding="0dp"
                android:layout_marginEnd="8dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />

            <Button
                android:id="@+id/delete_person_task"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:background="@drawable/style3_button_background"
                app:backgroundTint="@null"
                android:text="Personのtaskの削除(personId=1 and finished=true)"
                android:padding="0dp"
                android:textSize="11sp"
                tools:ignore="HardcodedText"
                style="?android:attr/buttonBarButtonStyle" />
        </LinearLayout>

    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
```

```kotlin:RealmTestService.kt
package com.example.androidtest

import android.util.Log
import io.realm.kotlin.Realm
import io.realm.kotlin.RealmConfiguration
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.types.EmbeddedRealmObject
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey

class Dog: RealmObject {
    @PrimaryKey
    var dogId: Int? = null
    var name: String = ""
    var age: Int = 0
}

class Person: RealmObject {
    @PrimaryKey
    var personId: Int? = null
    var name: String = ""
    var age: Int = 0
    var tasks: RealmList<Task> = realmListOf()
}

class Task: EmbeddedRealmObject {
    var taskId: Int? = null
    var name: String = ""
    var finished: Boolean = false
}

object RealmTestService {
    private var localRealm: Realm? = null

    val realm: Realm
        get() {
            if (localRealm != null) {
                return localRealm!!
            }

            val config = RealmConfiguration.Builder(
                schema = setOf(Dog::class, Person::class, Task::class))
                // 64バイトのキーを動的に作成する必要あり
                // キーの保存先も安全な場所に保存する必要あり
                // .encryptionKey(Base64.decode("abc...", Base64.DEFAULT)) // 暗号化キー
                .initialData {// DBの初期値
                    copyToRealm(Dog().apply {
                        dogId = 0
                        name = "いぬ"
                        age = 3
                    })
                    copyToRealm(Dog().apply {
                        dogId = 1
                        name = "うさぎ"
                        age = 2
                    })
                    copyToRealm(Dog().apply {
                        dogId = 2
                        name = "とり"
                        age = 2
                    })
                    copyToRealm(Person().apply {
                        personId = 0
                        name = "りょうたくん"
                        age = 20
                    })
                    copyToRealm(Person().apply {
                        personId = 1
                        name = "新たなりょうたくん"
                        age = 2
                        tasks = realmListOf(
                            Task().apply {
                                taskId = 0
                                name = "カレンダー作成"
                                finished = false
                            },
                            Task().apply {
                                taskId = 1
                                name = "アプリ申請"
                                finished = false
                            }
                        )
                    })
                }.build()

            localRealm = Realm.open(config)
            Log.d("Realm情報", "開いたRealm ${localRealm!!.configuration.name}")

            return localRealm!!
        }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/ebd7d7f5-e570-d5b9-833c-45b580c22fcb.png" width="300" />

![スクリーンショット 2024-05-10 12.26.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/8936e39f-3c56-00ec-d109-1deb01b164b8.png)

![スクリーンショット 2024-05-10 12.26.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/c924f606-1a88-b1ba-205b-51dfa27410f5.png)

![スクリーンショット 2024-05-10 12.26.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/2d55dafe-3f0a-b9ab-c017-b62f3ff69587.png)

![スクリーンショット 2024-05-10 12.27.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/d4b3eba9-8257-e674-d35b-75a0dc473f8d.png)

![スクリーンショット 2024-05-10 12.27.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/e359c5fe-4a50-7bdb-7f64-5ea99bb06f54.png)

![スクリーンショット 2024-05-10 12.27.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545849/7f71f670-bfa4-0854-bcc3-dadc2c1ecd05.png)