【Android】DB を使用する ~ SQLite 編 ~

■ はじめに

* Android の 標準データベースである、SQLiteを学ぶ。

* Realm については、以下の関連記事を参照のこと。

DB を使用する ~ Realm 編 ~
https://dk521123.hatenablog.com/entry/2018/09/01/213649

 ■ Android のDBについて

* Android の データベースは、SQLiteを標準サポート

 SQLiteを使うには...

色々な方法があると思うが、今回は、以下の2つの方法について扱う 

【1】SQLiteOpenHelper を使用する

Anko について

* Kotlinライブラリ Anko を利用する方法もあるが、
 Anko が非推奨になっている...

https://github.com/Kotlin/anko/blob/master/GOODBYE.md

 ■ ポイント

 SELECT文

Cursor cursor = db.query(DB_TABLE, cols,
  selection, selectionArgs, groupBy, having, orderBy);

第1引数 : テーブル名
第2引数 : 取得するテーブルの列を、文字列配列
第3引数 : where条件
第4引数 : where条件には、パラメータ?を使う事ができ、パラメータ値を文字列配列
第5引数 : GROUP BY
第6引数 : HAVING
第7引数 : ORDER BY

 ■ サンプル

Java

 レイアウト(画面)

ID   [__________(入力)__________]
Name [__________(入力)__________]
[ SELECT ] [ DELETE ]
[ INSERT ] [ UPDATE ]
[ (出力) ]

 レイアウト : activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="ID"
                android:id="@+id/idTextView" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/idEditText" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Name"
                android:id="@+id/nameTextView" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:id="@+id/nameEditText" />

        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Select"
                android:id="@+id/selectButton"
                android:onClick="selectButtonOnClick" />

            <Button
                style="?android:attr/buttonStyleSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Delete"
                android:id="@+id/deleteButton"
                android:onClick="deleteButtonOnClick" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Insert"
                android:id="@+id/insertButton"
                android:onClick="insertButtonOnClick" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Update"
                android:id="@+id/updateButton"
                android:onClick="updateButtonOnClick" />
        </TableRow>

        <TableRow
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Result"
                android:id="@+id/resultTextView" />
        </TableRow>
    </TableLayout>
</RelativeLayout>

 画面制御 : MainActivity.java

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.util.Map;

public class MainActivity extends AppCompatActivity {
    private SampleSQLiteOpenHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        this.dbHelper = new SampleSQLiteOpenHelper(super.getApplicationContext());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    public void selectButtonOnClick(View view) {

        String id = this.getId();
        String name = this.getName();

        Map<String, String> results = this.dbHelper.getSqlData(id, name);
        String output = "";
        for(Map.Entry<String, String> result : results.entrySet()) {
            output = output + "ID : " + result.getKey() + " / Name : " + result.getValue();
        }
        this.setResult(output);
    }

    public void insertButtonOnClick(View view) {
        String id = this.getId();
        String name = this.getName();

        boolean isSuccessful = this.dbHelper.insert(id, name);
        if (isSuccessful) {
            this.setResult("Successful");
        } else {
            this.setResult("Failed...");
        }

    }

    public void updateButtonOnClick(View view) {
        String id = this.getId();
        String name = this.getName();

        boolean isSuccessful = this.dbHelper.updateById(id, name);
        if (isSuccessful) {
            this.setResult("Successful");
        } else {
            this.setResult("Failed...");
        }
    }

    public void deleteButtonOnClick(View view) {
        String id = this.getId();
        String name = this.getName();

        boolean isSuccessful = this.dbHelper.deleteById(id, name);
        if (isSuccessful) {
            this.setResult("Successful");
        } else {
            this.setResult("Failed...");
        }
    }

    private String getId() {
        return ((EditText)super.findViewById(R.id.idEditText)).getText().toString();
    }

    private String getName() {
        return ((EditText)super.findViewById(R.id.nameEditText)).getText().toString();
    }

    private void setResult(String resultValue) {
        TextView resultTextView = (TextView)super.findViewById(R.id.resultTextView);
        resultTextView.setText(resultValue);
    }
}

 DB処理 : SampleSQLiteOpenHelper.java

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.util.HashMap;
import java.util.Map;

public class SampleSQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "sampledb";
    private static final int DB_VERSION = 1;
    private static final String DB_TABLE = "sampletable";
    private static final String CREATE_TABLE =
            "CREATE TABLE IF NOT EXISTS sampletable (_id TEXT PRIMARY KEY, _name TEXT);";
    private static final String DROP_TABLE = "DROP TABLE IF EXISTS sampletable;";

    public SampleSQLiteOpenHelper(Context context) {
        this(context, DB_NAME, null, DB_VERSION);
    }
    public SampleSQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    // データベースを作成した時に呼び出されるメソッド
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_TABLE);
    }

    // データベースをバージョンアップした時に呼び出されるメソッド
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(DROP_TABLE);
        this.onCreate(db);
    }

    public Map<String, String> getSqlData(String id, String name) {
        Map<String, String> results = new HashMap<>();
        try {
            SQLiteDatabase db = this.getWritableDatabase();

            String[] cols = { "_id", "_name" };
            String selection = null;
            String[] selectionArgs = null;
            if (!this.isNullOrEmpty(id) && !this.isNullOrEmpty(name)) {
                selection = "_id = ? AND _name = ?";
                selectionArgs = new String[] {id, name};
            } else if (!this.isNullOrEmpty(id) && this.isNullOrEmpty(name)) {
                selection = "_id = ?";
                selectionArgs = new String[] {id};
            } else if (this.isNullOrEmpty(id) && !this.isNullOrEmpty(name)) {
                selection = "_name = ?";
                selectionArgs = new String[] {name};
            }
            String groupBy = null;
            String having = null;
            String orderBy = null;
            Cursor cursor = db.query(DB_TABLE, cols,
                    selection, selectionArgs, groupBy, having, orderBy);
            boolean isEnd = cursor.moveToFirst();
            while (isEnd) {
                results.put(cursor.getString(0), cursor.getString(1));
                isEnd = cursor.moveToNext();
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            results = null;
        }
        return results;
    }

    public boolean insert(String id, String name) {
        boolean isSuccessful = false;
        try {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("_id", id);
            values.put("_name", name);
            db.insertOrThrow(DB_TABLE, null, values);
            isSuccessful = true;
        } catch (Exception ex) {
            ex.printStackTrace();
            isSuccessful = false;
        }
        return isSuccessful;
    }

    public boolean updateById(String id, String name) {
        boolean isSuccessful = false;
        try {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("_name", name);
            String whereClause = null;
            if (!this.isNullOrEmpty(id)) {
                whereClause = "_id = " + id;
            }
            db.update(DB_TABLE, values, whereClause, null);
            isSuccessful = true;
        } catch (Exception ex) {
            ex.printStackTrace();
            isSuccessful = false;
        }
        return isSuccessful;
    }

    public boolean deleteById(String id, String name) {
        boolean isSuccessful = false;
        try {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues values = new ContentValues();
            values.put("_name", name);
            String whereClause = null;
            if (!this.isNullOrEmpty(id)) {
                whereClause = "_id = " + id;
            }
            db.delete(DB_TABLE, whereClause, null);
            isSuccessful = true;
        } catch (Exception ex) {
            ex.printStackTrace();
            isSuccessful = false;
        }
        return isSuccessful;
    }

    private boolean isNullOrEmpty(String value) {
        return value == null || value.isEmpty();
    }
}

 参考資料

http://android.keicode.com/basics/sqlite-dataadapter.php

関連記事

DB を使用する ~ Realm 編 ~
https://dk521123.hatenablog.com/entry/2018/09/01/213649