Android で SQLite データベースと Room を簡単に使用する

目次

データベースを作成する

データベースをアップグレードする

 データベースに対する CRUD 操作

データの追加

 データを更新する

データを削除する

クエリデータ

トランザクションを使用する 

部屋の利用

 エンティティの定義

道を定義する

データベースの定義


データベースを作成する

データベース クラスを作成し、Book テーブルを作成します。まずテーブルを作成する SQL ステートメントを記述し、次にこのステートメントを onCreate で実行します。

class MyDatabaseHelper(val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {

    private val createBook = "create table Book (" +
            " id integer primary key autoincrement," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text," +
            "category_id integer)"


    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(createBook)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        
    }

}

activity_main.xml のコードを変更し、データベースを作成するためのボタンのみを追加します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
>

    <Button
            android:id="@+id/createDatabase"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Create Database"
    />


</LinearLayout>

最後に、MainActivity のコードを変更してデータベースを作成します。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
}
}

 このうち、 MyDatabaseHelper() のパラメータは、コンテキスト、データベース名、データベース バージョンです。データベースの作成は 1 回のみであり、再作成時にデータベースがすでに存在していることが判明した場合、再作成は行われません。

データベースをアップグレードする

カテゴリ テーブルがアップグレードされたデータベースに追加されると仮定すると、次のように記述できます。まずカテゴリ テーブルを作成する SQL ステートメントを記述し、次にこのステートメントを onUpgrade で実行し、MainActivity でデータベースのバージョン番号を 2 に変更し、アップグレードロジックが実行されます。各バージョンを判断する必要があることに注意してください。そうでない場合、バージョン 1 からバージョン 3 に直接アップグレードすると、バージョン 2 のアップグレード ロジックがスキップされます。


class MyDatabaseHelper(val context: Context, name: String, version: Int) : SQLiteOpenHelper(context, name, null, version) {

    private val createBook = "create table Book (" +
            " id integer primary key autoincrement," +
            "author text," +
            "price real," +
            "pages integer," +
            "name text," +
            "category_id integer)"

    private val createCategory = "create table Category (" +
            "id integer primary key autoincrement," +
            "category_name text," +
            "category_code integer)"

    override fun onCreate(db: SQLiteDatabase) {
        db.execSQL(createBook)
        db.execSQL(createCategory)
        Toast.makeText(context, "Create succeeded", Toast.LENGTH_SHORT).show()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        if (oldVersion <= 1) {
            db.execSQL(createCategory)
        }
        if (oldVersion <= 2) {
            db.execSQL("alter table Book add column category_id integer")
        }
    }

}

 データベースに対する CRUD 操作

データの追加

データを追加するには、挿入メソッドのパラメーターを (テーブル名、データが追加されない場合は空の列に自動的に null を割り当てる、および追加されるデータ) として使用し、最初に addData の ID を持つボタンを activity_main.xml に追加し次にSetこのボタンのクリックイベント。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
}
}

テーブルのidカラムは自己インクリメントに設定されているため、idカラムに値を代入する必要はありませんが、ここでの値の記述形式は簡略化でき、例えばvalues1は次のように記述できます。

val value1= contentValuesOf(「名前」を「ダ・ヴィンチ・コード」に、「著者」を「ダン・ブラウン」に、「ページ数」を 454 に、「価格」を 16.96)

 データを更新する

データを更新するには、パラメーター (テーブル名、更新するデータ、および更新する行を指定する 3 番目と 4 番目のパラメーター) を指定して update メソッドを使用し、最初に updateData という ID を持つボタンを activity_main.xml に追加から、このボタンのボタン クリックイベントを設定します。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
}
}

ここでの値の記述形式も簡略化できます。

データを削除する

データを更新するには、delete メソッドのパラメーターが使用されます (テーブル名、2 番目と 3 番目のパラメーターは削除する行を指定します)。まず、 activity_main.xml にdeleteData のID を持つボタンを追加してから、クリック イベントを設定します。このボタンの場合。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
}
}

クエリデータ

これは最も複雑な操作です。クエリはクエリ メソッドを使用します。デモンストレーションの便宜上、最も短いクエリ オーバーロード メソッドが使用されます。これには 7 つのパラメータがあります。(テーブル名、クエリする列/すべての列をクエリする場合はnull、 3 番目と 4 番目のパラメータはクエリする行を制約します。 / null はすべての行をクエリすることを意味します。 / null は group by 操作を実行しないことを意味します。 / null はフィルタしないことを意味します。クエリ結果の並べ替え方法を指定します /デフォルトの並べ替えを使用する場合は null ) このメソッドはカーソル オブジェクトを返します。まず、 ID が query Dataであるボタンを activity_main.xml に追加し、次にこのボタンのクリック イベントを設定します。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
        queryData.setOnClickListener {
            val db = dbHelper.writableDatabase
            // 查询Book表中所有的数据
            val cursor = db.query("Book", null, null, null, null, null, null)
            if (cursor.moveToFirst()) {
                do {
                    // 遍历Cursor对象,取出数据并打印
                    val name = cursor.getString(cursor.getColumnIndex("name"))
                    val author = cursor.getString(cursor.getColumnIndex("author"))
                    val pages = cursor.getInt(cursor.getColumnIndex("pages"))
                    val price = cursor.getDouble(cursor.getColumnIndex("price"))
                    Log.d("MainActivity", "book name is $name")
                    Log.d("MainActivity", "book author is $author")
                    Log.d("MainActivity", "book pages is $pages")
                    Log.d("MainActivity", "book price is $price")
                } while (cursor.moveToNext())
            }
            cursor.close()
        }
}
}

トランザクションを使用する 

データベースでトランザクションを使用すると、一連の操作がすべて完了するか、いずれも完了しないことが保証されます。まず、 activity_main.xml 内のreplace Dataという ID を持つボタンを追加し、次にこのボタンのクリック イベントを設定します。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val dbHelper = MyDatabaseHelper(this, "BookStore.db", 1)
        createDatabase.setOnClickListener {
            dbHelper.writableDatabase
        }
        addData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values1 = ContentValues().apply {
                // 开始组装第一条数据
                put("name", "The Da Vinci Code")
                put("author", "Dan Brown")
                put("pages", 454)
                put("price", 16.96)
            }
            db.insert("Book", null, values1) // 插入第一条数据
            val values2 = ContentValues().apply {
                // 开始组装第二条数据
                put("name", "The Lost Symbol")
                put("author", "Dan Brown")
                put("pages", 510)
                put("price", 19.95)
            }
            db.insert("Book", null, values2) // 插入第二条数据
        }
        updateData.setOnClickListener {
            val db = dbHelper.writableDatabase
            val values = ContentValues()
            values.put("price", 10.99)
            val rows = db.update("Book", values, "name = ?", arrayOf("The Da Vinci Code"))
            Toast.makeText(this, "rows is $rows", Toast.LENGTH_SHORT).show()
        }
        deleteData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.delete("Book", "pages > ?", arrayOf("500"))
        }
        queryData.setOnClickListener {
            val db = dbHelper.writableDatabase
            // 查询Book表中所有的数据
            val cursor = db.query("Book", null, null, null, null, null, null)
            if (cursor.moveToFirst()) {
                do {
                    // 遍历Cursor对象,取出数据并打印
                    val name = cursor.getString(cursor.getColumnIndex("name"))
                    val author = cursor.getString(cursor.getColumnIndex("author"))
                    val pages = cursor.getInt(cursor.getColumnIndex("pages"))
                    val price = cursor.getDouble(cursor.getColumnIndex("price"))
                    Log.d("MainActivity", "book name is $name")
                    Log.d("MainActivity", "book author is $author")
                    Log.d("MainActivity", "book pages is $pages")
                    Log.d("MainActivity", "book price is $price")
                } while (cursor.moveToNext())
            }
            cursor.close()
        }
        replaceData.setOnClickListener {
            val db = dbHelper.writableDatabase
            db.beginTransaction() // 开启事务
            try {
                db.delete("Book", null, null)
//                if (true) {
//                    // 在这里手动抛出一个异常,让事务失败
//                    throw NullPointerException()
//                }
                val values = cvOf("name" to "Game of Thrones", "author" to "George Martin", "pages" to 720, "price" to 20.85)
                db.insert("Book", null, values)
                db.setTransactionSuccessful() // 事务已经执行成功
            } catch (e: Exception) {
                e.printStackTrace()
            } finally {
                db.endTransaction() // 结束事务
            }
        }
}
}

部屋の利用

 Room はデータベースのネイティブ API をカプセル化したもので、Entity、Dao、Database の 3 つの部分で構成されます。エンティティはデータベース内のテーブルとして理解でき、Dao はデータベース操作のカプセル化です。データベースはデータベース内のキー情報を定義し、Dao アクセス エンティティ クラスを提供します。

まず、次の依存関係をソフトウェア レベルの Gradle に追加します。

apply plugin: 'kotlin-kapt'

    implementation "androidx.room:room-runtime:2.1.0"
    kapt "androidx.room:room-compiler:2.1.0"

 エンティティの定義


@Entity
data class User(var firstName: String, var lastName: String, var age: Int) {

    @PrimaryKey(autoGenerate = true)
    var id: Long = 0

}

データ クラスの前に @Entity アノテーションを追加してエンティティ クラスにします。次に、id 列の前に注釈 @PrimaryKey(autoGenerate = true) を追加し、id を主キーとして宣言し、自動インクリメントを設定します。

道を定義する

Dao レイヤーはビジネス ロジックがカプセル化されている場所で、ここに可能な CRUD 操作を記述してみてください。


@Dao
interface UserDao {

    @Insert
    fun insertUser(user: User): Long

    @Update
    fun updateUser(newUser: User)

    @Query("select * from User")
    fun loadAllUsers(): List<User>

    @Query("select * from User where age > :age")
    fun loadUsersOlderThan(age: Int): List<User>

    @Delete
    fun deleteUser(user: User)

    @Query("delete from User where lastName = :lastName")
    fun deleteUserByLastName(lastName: String): Int

}

ここでの挿入、更新、および削除操作は、デモを簡単にするために SQL ステートメントを記述しませんが、SQL ステートメントを使用して操作することもできます。@Query アノテーションがすべてのクエリ操作ではなく、最後の操作が削除操作であることがわかります。ここで @Delete の代わりに @Query アノテーションを使用する理由は、次のアノテーションが非エンティティ クラス パラメーターを使用し、 CRUD の非エンティティ クラス パラメーター 操作中に @Query アノテーションを一律に使用します。クエリ操作では、2 番目のクエリ操作と同様に、渡された変数を使用してデータを動的にクエリできます。

データベースの定義


@Database(version = 1, entities = [User::class, Book::class])
abstract class AppDatabase : RoomDatabase() {

    abstract fun userDao(): UserDao

    abstract fun bookDao(): BookDao

    companion object {

        private val MIGRATION_1_2 = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("create table Book (id integer primary key autoincrement not null, name text not null, pages integer not null)")
            }
        }

        private val MIGRATION_2_3 = object : Migration(2, 3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("alter table Book add column author text not null default 'unknown'")
            }
        }

        private var instance: AppDatabase? = null

        @Synchronized
        fun getDatabase(context: Context): AppDatabase {
            instance?.let {
                return it
            }
            return Room.databaseBuilder(context.applicationContext, AppDatabase::class.java, "app_database")
                .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                .build().apply {
                instance = this
            }
        }
    }

}

ここでは、 @Database アノテーションを使用して、これがデータベース エンティティ クラスであることを宣言しています。次に、データベースのバージョン含まれるエンティティ クラスを書き込みます。次に、データベースのアップグレード ロジックがコンパニオン オブジェクト { } で定義されます。ここで、バージョン 1 からバージョン 2、およびバージョン 2 から 3 のアップグレード ロジックが定義され、通常は.addMigrations ( MIGRATION_1_2 、 MIGRATION_2_3 )を置き換えるために .fallbackToDestructiveMigration() が使用されます。)、このメソッドをアップグレードすると、現在のデータベースが破棄されて再作成されるため、当面は複雑なアップグレード ロジックを記述する必要はありません (開発後に適時に置き換えることを忘れないでください)また、データベースの操作は時間のかかる操作なので、通常はメインスレッドでは操作できず、サブスレッドに置くしかありませんが、開発の都合上、 を追加することができます。データベース.build() を実行する前に、allowMainThreadQueries() を実行しますこのメソッドにより、メイン スレッドで操作を実行できるようになります(開発後は必ず削除してください)次に、データベースのインスタンスをキャッシュするインスタンスを定義し、getDatabaseメソッドで空の処理を判断し、空でない場合は返却、空でない場合はRoom.databaseBuilderメソッドを呼び出して直接構築します。別のインスタンスを作成し、それをインスタンスに割り当ててから戻ります。Room.databaseBuilderの最初のパラメータはapplicationContext を使用する必要があることに注意してください。そうしないと、簡単に表示されてしまいます。メモリ リークの場合、2 番目のパラメータはデータベースのクラス タイプ、3 番目のパラメータはデータベースの名前です。

最後に、実際にデータベースを操作する様子を見てみましょう。

まず CRUD 操作用の 4 つのボタンを定義します。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    
    <Button
        android:id="@+id/addDataBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Add Data"/>

    <Button
        android:id="@+id/updateDataBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Update Data"/>

    <Button
        android:id="@+id/deleteDataBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Delete Data"/>

    <Button
        android:id="@+id/queryDataBtn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Query Data"/>



</LinearLayout>


class MainActivity : AppCompatActivity() {

  
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
     
        val userDao = AppDatabase.getDatabase(this).userDao()
        val user1 = User("Tom", "Brady", 40)
        val user2 = User("Tom", "Hanks", 63)
       
        addDataBtn.setOnClickListener {
            thread {
                user1.id = userDao.insertUser(user1)
                user2.id = userDao.insertUser(user2)
            }
        }
        updateDataBtn.setOnClickListener {
            thread {
                user1.age = 42
                userDao.updateUser(user1)
            }
        }
        deleteDataBtn.setOnClickListener {
            thread {
                userDao.deleteUserByLastName("Hanks")
            }
        }
        queryDataBtn.setOnClickListener {
            thread {
                for (user in userDao.loadAllUsers()) {
                    Log.d("MainActivity", user.toString())
                }
            }
        }
       
}

さて、データベースの基本的な操作はここまでなので、基礎学習としては十分でしょう。

おすすめ

転載: blog.csdn.net/liny70858/article/details/127345412