kotlinでandroid入門 データベース : SQLite

### 1. データベースの生成クラス

```userDB_Helper.kt
// データベースの生成を管理する
class userDB_Helper (var mContext: Context?) : SQLiteOpenHelper(mContext, DATABASE_NAME, null, DB_VERSION) {
    //  SQLiteOpenHelper
    // 第１引数 :
    // 第２引数 : データベースの名称
    // 第３引数 : null
    // 第４引数 : データベースのバージョン

    override fun onCreate(db: SQLiteDatabase?) {
        // テーブルがなかったときに が呼ばれる
        // execSQL で　クエリSQL文を実行 これでDBの構造が決定
        db?.execSQL(
                "CREATE TABLE " + DB_TABLE_NAME + " ( " +
                        "_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
                        "type integer not null, "+
                        "date text not null, " +
                        "memo text not null " +
                        ");")
    }
    override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
        // バージョンが変わった時に実行される
        db?.execSQL("DROP TABLE IF EXISTS " + DB_TABLE_NAME + ";")
        onCreate(db);
        // 今回は,一度消して、作り直ししてます　
    }
}
```

### 2.データベース操作クラス

特に必要なクラスではありませんが、SQLiteOpenHelperのクラスを ラップして操作を提供したほうが扱いやすいです

例として

```userDB_Adapter.kt
class userDB_Adapter(mContext: Context) {
    private val db: SQLiteDatabase
    private val uaerDB : userDB_Helper
    init {
        uaerDB = userDB_Helper(mContext)  // DB生成
        db = uaerDB.getWritableDatabase()  
    }

    // 指定 キーのデータあり?
    fun isRecord( type:Int ,day:Int ) :Boolean {
        val selectQql : String = "SELECT count(*) as cnt FROM " + DB_TABLE_NAME + " where type = ? and date= ? "

        val cursor: Cursor = db.rawQuery(selectQql, arrayOf(type.toString(),day.toString()))
        cursor.moveToFirst()
        val count = cursor.getInt(cursor.getColumnIndex("cnt"))
        cursor.close()
        return if ( 0 < count ) { true } else { false }
    }

    // １レコード 追加
    fun addRecord(type:Int , day:Int ,memo:String) {
        val values = ContentValues()
        values.put("memo", memo)
        values.put("date", day )
        values.put("type", type)

        // insertOrThrow()
        // 第1引数はテーブル名
        // 第2引数はデータを挿入する際にnull値が許可されていないカラムに代わりに利用される値を指定(?)
        // 第3引数は ContentValue(データ)
        db.insertOrThrow(DB_TABLE_NAME, null, values)
    }

    // キー(Type,date)を指定してmemoを取得
    fun getMemo( type : Int, day : Int) : String {
        val selectQql : String = "select * from " + DB_TABLE_NAME + " where type = ? and date= ? "
        // 第一引数にある?の部分に
        // 第二引数が入る（複数可能、先に指定した順)
        val cursor: Cursor = db.rawQuery(selectQql, arrayOf(type.toString(),day.toString()))
        var Result : String = ""
        try {
            if (cursor.moveToNext()) {
                Result = cursor.getString(cursor.getColumnIndex("memo"))
            }
        } finally {
            cursor.close()
        }
        return Result
    }

    // キー(Type,date)を指定してmemoを修正
    fun updateMemo(type : Int, day : Int, memo : String ) {
        val values : ContentValues = ContentValues()
        values.put("memo",memo)
        // 第二引数がupdateする条件
        // 第三引数の? に第四引数が置き換わる
        db.update(DB_TABLE_NAME, values, "type=? AND date=? ", arrayOf(type.toString(),day.toString()))
    }

    // キーを指定し、１レコード削除
    fun deleteRecord(type : Int, day : Int) {
        db.delete(DB_TABLE_NAME, "tepe=? AND date=? ", arrayOf(type.toString(),day.toString()))
    }
}
```

上記コードは SQL文に対して、exeSQL()じゃなくメゾットを使ってみた
> 1. SELECT文                : query()
>                            : rawQuery()
>                            : queryWithFactory()
>                            : rawQueryWithFactory()
> 2. INSERT文                : insert()
>                            : insertOrThrow()
>                            : insertWithOnConflict()
> 3. UPDATE文                : update()
>                            : updateWithOnConflict()
> 4. DELETE文                : delete()


### 3. データベース/レコードの操作


#### 3.1. データベース生成
AppCompatActivityクラスの中で

```
    // クラス内で使えるように
    lateinit private var userDB : userDB_Adapter
    override fun onCreate(savedInstanceState: Bundle?) {
        // DBを呼び出している
        userDB = userDB_Adapter(this)
```

#### 3.2 レコード読込

```
    val memo = userDB.getMemo(type ,day)
```

#### 3.3 レード書き込み

```
    if ( false == userDB.isRecord((edit_type.text.toString()).toInt(),day )) {
        // 指定キーのデータなければ追加
        userDB.addRecord((edit_type.text.toString()).toInt(), day, edit_memo.text.toString())
    } else {
        // あれば修正
        userDB.updateMemo((edit_type.text.toString()).toInt(), day, edit_memo.text.toString())
    }
```            

#### 3.4 レード削除

```
    userDB.deleteRecord(type ,day)
```


#### 5. 日付をIntで使ってました

データベースの使い方ではないですが、日付をIntで使ってました
Androidでは無いのですが、日付型とか文字列の場合、過去にちょと苦しんだ経験があって

```
    // 日付からDBのキーを作成してる
    private val YBASE : Int = 2018
    // 日付をSQLでIntで使うため (なんとなく)
    private fun stringDateToInt( sDate : String ) : Int? {
        val f = SimpleDateFormat("yyyy/MM/dd")
        val fy = SimpleDateFormat("yyyy")
        val fd = SimpleDateFormat("D")
        try {
            val mDate = f.parse(sDate)
            val y : Int = fy.format(mDate).toInt()
            val ycnt : Int = if ( YBASE <= y ) { y - YBASE } else { 0 }
            val dcnt : Int = fd.format(mDate).toInt()
            return ( ycnt * 1000 ) + dcnt
        } catch (e: ParseException) {
            e.printStackTrace()
            return null
        }
    }
```

なのでサンプルでは日付がIntになってます