【kotlin】データベースアクセス　SQLiteデータベース

#■SQLiteデータベースの使い方

###1 ■データベースヘルパーのクラスを作る。
[java] -> [com.websarva.wings.android.databasesample]
右クリック
[NEW] -> [Kotlin File/Class]選択
クラス名を記述

###2 ■SQLiteOpenHelper
SQLiteOpenHelperクラスを継承するサブクラスを作る。引数は4個
保持しているDB接続オブジェクトを使ってSQLiteデータベースを作って管理するクラス。

``` kotlin
SQLiteOpenHelper(context:Context?,name: String?, factory: SQLiteDatabase.CursorFactory?,version: Int)
```
▼第一引数 context:Context?
ヘルパークラスを使う画面
コンテキストは他の引数と違いクラス内で用意できない
下記のDatabaseのコンストラクタの引数として定義する。

▼第二引数　name: String?
使用するデータベース名

▼第三引数　factory: SQLiteDatabase.CursorFactory?
データを入れて並べるカーソルを作る。
　通常はnull

▼第四引数 version: Int
バージョン情報の定数
1から始まる定数を指定する。


``` kotlin
class DatabaseHelper(context:Context): SQLiteOpenHelper(context,DATABASE_NAME,null,DATABASE_VERSION) {
}
```

###3 ■データベース名とバージョン情報を記載

■クラス内のprivate定数を宣言するためにcompanision objectブロックとする。
▼第2引数のデータベース名
▼第4引数のバージョン情報の定数
を決める。

``` kotlin
class DatabaseHelper(context:Context): SQLiteOpenHelper(context,DATABASE_NAME,null,DATABASE_VERSION) {

 companion object {
    //使用するデータベース名
    private const val DATABASE_NAME = "好きな名前.db"
    //バージョン情報の定数
    private const val DATABASE_VERSION = 1
  }
}
```

###4 ■onCreate()メソッド データベースを作成


親クラスのコンストラクタで指定したデータベースが存在しない時
つまり初期状態の時に1回だけ発動。
中でテーブルが作成する。

▼第一引数 db: SQLiteDatabase?
データベース接続オブジェクトそのもの

▼StringBuilder()
appendで文字を繋げる.
strignでは+で文字を繋げていた。
内容の変更が可能
文字連結で使う。


▼execSQL()メソッド
CREATE TBLE文などのDDL文を実行する。
 

``` kotlin

class DatabaseHelper(context:Context): SQLiteOpenHelper(context,DATABASE_NAME,null,DATABASE_VERSION) {

 companion object {
    private const val DATABASE_NAME = "好きな名前.db"
    private const val DATABASE_VERSION = 1
  }

//SQLiteDatabaseオブジェクトを通じてSQLiteデータベースを生成
override fun onCreate(db: SQLiteDatabase?) {
        //文字連結
        val sb = StringBuilder()
        //テーブル名
        sb.append("CREATE TABLE hoge(")
        //カラム名 主キー
        sb.append("_ib INTEGER PRIMARY KEY,")
        //カラム名 TEXT型
        sb.append("name TEXT,")
        //カラム名 TEXT型
        sb.append("note TEXT")
        //閉じる
        sb.append(");")
        //文字列型にキャスト
        val sql = sb.toString()
        //データベース接続オブジェクトに作ったテーブルを入れる。
        db.execSQL(sql)
    }
}
```

#####▼StringBuilder()を使って文字連結した結果
■PRIMARY KEY:　主キー
●中身が被らない
●中身がnullを許さないもの

■_id
Androidではカラム名に_idとつければ自動的に主キーと判定する。

``` kotlin
"CREATE TABLE hoge("_ib INTEGER PRIMARY KEY,name TEXT,note TEXT")"
```
テーブルができる。

| _id |name | note|
|:-----------:|:------------:|:------------:|
|      |      |   |

###5 ■onUpgrade()メソッド
抽象メソッド

内部のデータベースのバージョン番号とコンストラクタの引数で渡される番号に違いがある場合に発動

処理が不要な時でも抽象メソッドなので記述は必要

``` kotlin

class DatabaseHelper(context:Context): SQLiteOpenHelper(context,DATABASE_NAME,null,DATABASE_VERSION) {

 companion object {
    private const val DATABASE_NAME = "好きな名前.db"
    private const val DATABASE_VERSION = 1
  }

//SQLiteDatabaseオブジェクトを通じてSQLiteデータベースを生成
override fun onCreate(db: SQLiteDatabase?) {
        val sb = StringBuilder()
        sb.append("CREATE TABLE hoge(")
        sb.append("_ib INTEGER PRIMARY KEY,")
        sb.append("name TEXT,")
        sb.append("note TEXT")
        sb.append(");")
        val sql = sb.toString()
        db.execSQL(sql)
    }
//抽象メソッド
override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
    }
}
```

###6 ■DBオブジェクトの解放
解放：メモリに溜まったゴミをすてる。
MainActibity.ktで行う

■onDestroy()
アクティビティ終了時に発動するメソッドここで解放する。
close()を書くsuper.onDestroy()の前に

``` kotlin
//MainActibity.kt
class MainActivity : AppCompatActivity() {
    //プロパティとしてインスタンスを生成
    private val _helper = DatabaseHelper(this@MainActivity)

    override fun onDestroy() {
        //ヘルパーオブジェクトの解放処理
        _helper.close()
        //アクティビティ終了
        super.onDestroy()
    }

```

###7　■データの更新処理　①
データがデータベースにあるかどうかにより処理が変わる。

■INSERT(登録処理)
もしデータがないなら登録する。

■UPDATE(更新処理)
もしデータがあるなら更新する。

他のやり方は下記

■DELETE(消去)
いったんデータを消去

■INSERT(登録処理)
データ消去してからデータの登録

①ヘルパーオブジェクトからデータベース接続オブジェクトをもらう。

``` kotlin
//MainActibity.kt
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    // ボタンのタップ時にデータ更新するとする。
    fun onSaveButtonClick(view: View) {
     //データベースヘルパーオブジェクトからデータベース接続オブジェクトを取得
     val db = _helper.writableDatabase
 }
}
```

###8 ■データ更新処理 ②DELETE編

②SQL文を作成する。
　?: 変数が後から挿入される箇所(=バインド変数)

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     //SQL文 DELETE文　？は変数によって値が変わる。
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
 }
} 
```

###9 ■データ更新処理 ③DELETE編

③ステートメントオブジェクトをもらう
データベース接続オブジェクトのcompileStatement()を使う
引数に作成したSQL文を入れる。

■ステートメント
SQL文を実行するオブジェクト

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"

    //ステートメントオブジェクトを使用
    val stmt = db.compileStatement(sqlDelete)
 }
} 
```

###10 ■データ更新処理 ④DELETE編

④変数をバインドする。
ステートメントオブジェクトのbind〇〇()メソッドを使う
SQL文中に作成した?に入れ込む

〇〇はデータ型によって変わる。
引数は2個入る。

▼第一引数
?の順番
▼第二引数
埋め込む値

SQL文に?がない時はいらない

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)

    //変数のバインド
    stmt.bindLong(1, _itemlId.toLong())
 }
} 
```

###11 ■データ更新処理 ⑤DELETE編

⑤消去SQLを実行する。

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)
     stmt.bindLong(1, _itemlId.toLong())
     //消去SQLの実行
     stmt.executeUpdateDelete()
  }
} 
```

###12 ■データ更新処理　①INSERT編

①INSERT文のSQL文を書く
DELETE文の後に
INSERT文を使う

``` SQL
//基本のINSERT文
INSERT INTO データベース名(カラム名1,カラム名2,カラム名3) VALUES(値1、値2,値3)
```

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     //DELETE文
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)
     stmt.bindLong(1, _itemlId.toLong())
     stmt.executeUpdateDelete()

     //INSERT文
     val sqlINSERT = "INSERT INTO hoge(_id,name,note) VALUES(?,?,?)"
  }
} 
```

###13 ■データ更新処理　②INSERT
②ステートメントオブジェクトを使用

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     //DELETE文
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)
     stmt.bindLong(1, _itemlId.toLong())
     stmt.executeUpdateDelete()

     //INSERT文
     val sqlINSERT = "INSERT INTO hoge(_id,name,note) VALUES(?,?,?)"
     //DELETE文で作った変数stmtを更新sqlINSERTを入れる。 
     stmt = db.compileStatement(sqlINSERT)
  }
} 
```

###14 ■データ更新処理　③INSERT
③変数バインドを用意

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     //DELETE文
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)
     stmt.bindLong(1, _itemlId.toLong())
     stmt.executeUpdateDelete()

     //INSERT文
     val sqlINSERT = "INSERT INTO hoge(_id,name,note) VALUES(?,?,?)" 
     stmt = db.compileStatement(sqlINSERT)
     //変数バインドを用意　それぞれ?に入る。第一引数は？の順番 第二引数は値
     stmt.bingLong(1, _itemId.toLong())
     stmt.bingString(2, _itemName)
     stmt.bingString(3, note)
  }
} 
```

###15 ■データ更新処理　③INSERT
③INSERTSQL文を実行！！

``` kotlin
class MainActivity : AppCompatActivity() {
    private val _helper = DatabaseHelper(this@MainActivity)

    fun onSaveButtonClick(view: View) {
     val db = _helper.writableDatabase
     //DELETE文
     val sqlDelete = "DELETE FROM hoge WHERE _id = ?"
     val stmt = db.compileStatement(sqlDelete)
     stmt.bindLong(1, _itemlId.toLong())
     stmt.executeUpdateDelete()

     //INSERT文
     val sqlINSERT = "INSERT INTO hoge(_id,name,note) VALUES(?,?,?)" 
     stmt = db.compileStatement(sqlINSERT)
     stmt.bingLong(1, _itemId.toLong())
     stmt.bingString(2, _itemName)
     stmt.bingString(3, note)
     //これでインサート文を実行します。
     stmt.executeInsert()
  }
} 
```

###16 ■データ取得処理　①

■データベースオブジェクトからデータベース接続オブジェクトを取得
writableDatabase

■SELECT文でデータを取得
Androidでは変数バインドが使いにくいため文字列テンプレートを直接入れ込む

``` SQL
SELECT カラム名 FROM テーブル名　WHERE 条件
```

``` kotlin
//データベース接続オブジェクトの取得
val db = _helper.writableDatabase

//SELECT文でデータを指定
val sql = "SELECT * FROM hoge WHERE _id = ${_itemId}"
```

###17 ■データ取得処理　②

②SQLの実行

■SELECT文の実行にはSQLiteDatabaseクラスの
rawQuery()メソッドを使う

▼第一引数
SQL文字列を渡す

▼バインド変数用のString配列
バインド変数を使わない場合はnull

``` SQL
SELECT カラム名 FROM テーブル名　WHERE 条件
```

``` kotlin
val db = _helper.writableDatabase
val sql = "SELECT * FROM hoge WHERE _id = ${_itemId}"

//SQL文の実行
val cursol = db.rawQuery(sql,null)
```

###18 ■データ取得処理　③

③データベースから取得したデータを入れる空の変数を用意
データが存在しない場合の初期値としても使う

``` kotlin
val db = _helper.writableDatabase
val sql = "SELECT * FROM hoge WHERE _id = ${_itemId}"
val cursol = db.rawQuery(sql,null)

//データベースを取得する空の変数
val note = ""

```

###19 ■データ取得処理　④

④SQL実行の戻り値であるカーソルオブジェクトをループさせて中身のデータを取得

■テーブルは表みたいになっている。
はじめの行を0行目とする。
moveText()メソッドの効果で1行進む
進んだ先にデータが存在する場合戻り値としてtrueを返す。
進んだ先にデータがない場合は戻り値としてfalseを返す。

| _id |name | note |
|:-----------:|:------------:|:------------:|
|    |     |      |
|    |    |    |
|   |    |       |
|   |  |     |

■while(cursol.moveToNext()){}
行データがなくなるまで1行づつデータを取得できる。

■cursol.getColumnIndex("note")
引数にカラム名を指定するとそのカラムのインデックスを取得する。
インデックスを使い適切なデータを取得する。

``` kotlin
val db = _helper.writableDatabase
val sql = "SELECT * FROM hoge WHERE _id = ${_itemId}"
val cursol = db.rawQuery(sql,null)
val note = ""
//テーブル1行づつのデータを見る
while(cursol.moveToNext()){
//カラムのインデックス番号を取得
val indexNote = cursol.getColumnIndex("note")
//インデックス番号を使い適切なデータを取得
note = cursol.getString(indexNote)
}
```

後は取得したデータを使い表示したりします。
以上がデータベースアクセスになります。