目次
データベースを作成する
データベース クラスを作成し、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())
}
}
}
}
さて、データベースの基本的な操作はここまでなので、基礎学習としては十分でしょう。