社内se × プログラマ × ビッグデータ

プログラミングなどITに興味があります。

Android(kotlin) 読み込んだテキストを SQLite に挿入

編集・作成するファイル
  • words.csv (読み込むテキスト)
  • WordFromText.kt (エンティティクラス)
  • WordManager.kt (テキストを読み込む)
  • DBContract.kt (定数定義用)
  • DatabaseHelper.kt (データベースヘルパークラス)
  • MainActivity.kt (WordManager と DatabaseHelper を操作)
words.csv 読み込むファイルを用意する

以下のようなテキストファイルを用意します。
ID と 英単語 と その日本語訳が書かれています。
それぞれタブ(TAB)で区切られているとします。

1	abstracted	 抽出された / ぼんやりした
2	counterfoil	 小切手(為替の)控え 
3	northbound	 北方行きの 
WordFromText.kt (読み込んだデータを格納するエンティティクラス) を作成する

今回、SQLite のテーブルに対して、INSERT 文を使用してデータを挿入しますが、挿入時に ID 列を指定しなかった場合、自動的に採番されて挿入されます。
ただ、同じデータを挿入することを避けるため、ID 列を明示的に指定しながら挿入していくことにしています。

class WordFromText(
    val _id: Int,
    val english: String,
    val japanese: String
) {
}
WordManager.kt (ファイル読み込み用クラス) を作成する
class WordManager(context: Context) {
    private val myContext = context
    private var wordList: MutableList<WordFromText> = mutableListOf()

    fun getWordsFromText(): List<WordFromText> {
        Log.d("MyLog at WordManager", "Called getWordsFromText()")
        // Read text file & create objects
        myContext.assets.open("words.csv").reader(charset = Charsets.UTF_8).useLines {lines -> lines.forEach {
            val parts =  it.split("\t")
            wordList.add(WordFromText(parts[0].toInt(), parts[1], parts[2])) }}
        return wordList
    }
}
DBContract.kt (データベース関連の定数を定義する)

必須ではないですが、繰り返し使用する定数のため定義しています。

object DBContract {
    class WordsEntity: BaseColumns {
        companion object {
            const val TABLE_NAME = "words"
            const val _ID = "_id"
            const val ENGLISH = "english"
            const val JAPANESE = "japanese"
            const val COUNT_QUESTIONS = "count_questions"
            const val COUNT_CORRECT_ANSWERS = "count_correct_answers"
        }
    }
}
DatabaseHelper.kt (データベースヘルパークラス)

"application" という名前のデータベースに "words" という名前のテーブルを作成します。
"_id", "english", "japanese", カラムのほかに、"count_questions", "count_correct_answers" というカラムも入っています。
これらは default 値を0で設定しています。
データの挿入SQLとして、"INSERT OR IGNORE " と指定することで、仮にすでに存在しているIDで登録しようとしても、無視(SKIP)するようにします。

class DatabaseHelper(context: Context): SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {
    // To declare private variable
    companion object {
        // Name of database file
        private const val DATABASE_NAME = "application.db"
        // Version of database
        private const val DATABASE_VERSION = 1
    }

    override fun onCreate(db: SQLiteDatabase) {
        Log.i("DatabaseHelper", "Called onCreate()")
        createWordsTable(db)
    }

    private fun createWordsTable(db: SQLiteDatabase) {
        Log.i("DatabaseHelper", "Called createWordsTable()")
        // SQL statement to create words table
        val sb = StringBuilder()
        sb.append("CREATE TABLE " + DBContract.WordsEntity.TABLE_NAME + "(")
        sb.append(DBContract.WordsEntity._ID + " INTEGER PRIMARY KEY,")
        sb.append(DBContract.WordsEntity.ENGLISH + " TEXT,")
        sb.append(DBContract.WordsEntity.JAPANESE + " TEXT,")
        sb.append(DBContract.WordsEntity.COUNT_QUESTIONS + " INTEGER DEFAULT 0,")
        sb.append(DBContract.WordsEntity.COUNT_CORRECT_ANSWERS + " INTEGER DEFAULT 0")
        sb.append(");")

        // Execute the SQL
        db.execSQL(sb.toString())
        Log.i("DatabaseHelper", "Created words table.")
        sb.clear()

        // SQL statement to create index
        val sb2 = StringBuilder()
        sb2.append("CREATE INDEX english_index on words(english);")

        // Execute the SQL
        db.execSQL(sb2.toString())
        Log.i("DatabaseHelper", "Created english_index on words table.")
        sb2.clear()
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        Log.i("DatabaseHelper", "Called onUpgrade()")
    }

    fun insertWord(word: WordFromText) {
        // Get database connection object
        val db = writableDatabase

        // Statement for Insert
        val sqlInsert = "INSERT OR IGNORE into " +
                DBContract.WordsEntity.TABLE_NAME + " (" +
                DBContract.WordsEntity._ID +"," +
                DBContract.WordsEntity.ENGLISH +"," +
                DBContract.WordsEntity.JAPANESE+") values (?, ?, ?)"
        var stmt = db.compileStatement(sqlInsert)
        stmt.bindLong(1, word._id.toLong())
        stmt.bindString(2, word.english)
        stmt.bindString(3, word.japanese)
        stmt.executeInsert()
    }
}
MainActivity.kt から WordManager と DatabaseHelper を操作

WordManager でテキストファイルを読み込んだ後、dbHelper で1行ずつデータを挿入していきます。

(省略)
        val wm = WordManager(this@LessonActivity)
        val words = wm.getWordsFromText()
        val dbHelper = DatabaseHelper(this@LessonActivity)
        words.forEach {
            Log.d("MyLog at LessonActivity", "Got _ID: ${it._id}, English : ${it.english}, Japanese : ${it.japanese}")
            dbHelper.insertWord(it)
        }
(省略)
ビルド・実行

Run ボタンをクリックし、エミュレーターを起動します。
Logcat には以下のようなメッセージが表示されます。
f:id:blueskyarea:20200507190722p:plain

また、テーブル内にもデータが挿入されていることが確認できます。
f:id:blueskyarea:20200509132603p:plain

作成した index 情報は以下のコマンドで確認ができます。

select * from sqlite_master where type = 'index';

f:id:blueskyarea:20200509132741p:plain