android SQLITE

 

FROM https://monoist.itmedia.co.jp/mn/articles/1209/13/news001.html

Androidを採用するメリットの1つが、データベースを利用したアプリケーションを簡単に作れることだ。今回は、Androidアプリケーションで使用できるデータベース「SQLite」の基本操作を紹介する。

SQLiteデータベースについて

 Androidには、SQLiteというデータベースが組み込まれており、アプリケーションから自由に利用できる。

 SQLiteは、Android専用に作られたものではない。組み込みアプリケーションなどで使用されている軽量データベースである。ライブラリとしてアプリケーションに直接組み込めるので、DBMS(DataBase Management System)サーバなどを介さずに手軽に使用できるのが大きな特徴だ。

 機能的には、一般的なデータベースと同様に「検索」「挿入」「更新」「削除」といった一通りの処理が行える他、バイナリデータの格納、トランザクションなども備える。

SQLiteDatabaseクラスとSQLiteOpenHelperクラス

 Androidアプリケーションで、データベース操作を行うには、「SQLiteDatabase」クラスを使用する。そして、このSQLiteDatabaseクラスのインスタンスを取得するための手順を簡略化してくれるのが、「SQLiteOpenHelper」クラスだ。

 データベースを使用するには、まずデータベースを保存するファイルを作成し、次にデータを格納するテーブルを定義し……といった準備が必要になる。SQLiteOpenHelperクラスを使用することで、こうした煩わしい初期処理を自動化できる。

 SQLiteOpenHelperは、abstractなクラスであり、以下のような実装を追加しておくと、必要なタイミングでデータベースの初期処理を実行してくれる。

  • データベース名(=データベースを保存するファイル名)
  • データベースのバージョン
  • テーブルの定義や、初期データの投入処理
  • データベースのバージョンアップ時の処理

SQLiteOpenHelperの実装例

 以下に、SQLiteOpenHelperのサブクラスの実装例を示す。この例では、“sample.db”という名前のデータベースを作成し、“name_book_table”テーブルを定義している。“name_book_table”は、人の名前(name)と年齢(age)を格納するための簡単なテーブルだ。

    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
 
 
    public class NameBookDBHelper extends SQLiteOpenHelper {
 
        // コンストラクタ
        public NameBookDBHelper( Context context ){
            // 任意のデータベースファイル名と、バージョンを指定する
            super( context, "sample.db", null, 1 );
        }
 
 
        /**
         * このデータベースを初めて使用する時に実行される処理
         * テーブルの作成や初期データの投入を行う
         */
        @Override
        public void onCreate( SQLiteDatabase db ) {
            // テーブルを作成。SQLの文法は通常のSQLiteと同様
            db.execSQL(
                    "create table name_book_table ("
                    + "_id  integer primary key autoincrement not null, "
                    + "name text not null, "
                    + "age  integer )" );
            // 必要なら、ここで他のテーブルを作成したり、初期データを挿入したりする
        }
 
 
        /**
         * アプリケーションの更新などによって、データベースのバージョンが上がった場合に実行される処理
         * 今回は割愛
         */
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // 取りあえず、空実装でよい
        }
    }


 データベースのファイル名とバージョン番号は、コンストラクタで指定している。このクラス(NameBookDBHelper)を使って実際にデータベースをオープンする際、万一、指定した名前のファイルが存在しない場合は自動的に作成してくれる。

 データベースファイルを作成した直後に、onCreate()メソッドが呼ばれ、そこに記述されているテーブル作成の処理などが実行される。なお、テーブルの作成には、SQLiteDatabaseのexecSQL()メソッドを使用している。execSQL()は、SQLをそのまま実行するメソッドだ。

 onUpgrade()メソッドは、今回は特に何も実装していない。ソースコード上のコメントにも記述したが、このメソッドはデータベースバージョンの更新時に実行される処理なので今回は割愛している。

データベースのオープン

 SQLiteOpenHelper(のサブクラス)を使って、実際にデータベースを開くには、getWritableDatabase()メソッドを使用する。

        // Helperを使用してデータベースを開く
        NameBookDBHelper dbHelper = new NameBookDBHelper( context );
        SQLiteDatabase db = dbHelper.getWritableDatabase();


 無事データベースのオープンに成功すると、SQLiteDatabaseのオブジェクトが返ってくる。このオブジェクトを使用して、データベースのさまざまな処理を実行する。



FROM https://monoist.itmedia.co.jp/mn/articles/1209/13/news001_2.html

データベース操作

挿入

 まずは、データの挿入について説明する。“name_book_table”テーブルにデータを1件追加するための処理は、以下のように記述する。


        /** エントリ追加 */
        private void doAddEntry( SQLiteDatabase db, String name, int age ){
            // 挿入するデータはContentValuesに格納
            ContentValues val = new ContentValues();
            val.put( "name", name );
            val.put( "age" , age  );
 
            // “name_book_table”に1件追加
            db.insert( "name_book_table", null, val );
        }


 データの挿入には、SQLiteDatabaseクラスのinsert()メソッドを使用する。第1引数にはテーブル名を指定し、第2引数には取りあえずnullを指定する。第3引数は実際に挿入するデータの指定となるが、これはあらかじめContentValuesというデータクラスに格納した状態で渡す。

 これ以外にも、テーブル定義作成の時に使ったexecSQL()メソッドを使用する方法もある。

    db.execSQL( "insert into name_book_table(name,age) values( 'Tips太郎', 30 )" );


 ただ、insert()メソッドが挿入成功件数を返すのに対し、execSQL()メソッドは値を返さないという違いがある。そのため、基本的にはinsert()メソッドを使った方がよさそうだ。

検索

 次に、データの検索について。検索は、SQLiteDatabaseのquery()メソッドを使用する。検索結果は、Cursorオブジェクトとして返されるのだが、このCursorオブジェクトから具体的な検索結果を取り出すところが少し複雑だ。

 以下に、検索処理の例を示す。

        /** 年齢が一致するデータを検索 */
        private String searchByAge( SQLiteDatabase db, int age ){
            // Cursorを確実にcloseするために、try{}~finally{}にする
            Cursor cursor = null;
            try{
                // name_book_tableからnameとageのセットを検索する
                // ageが指定の値であるものを検索
                cursor = db.query( "name_book_table", 
                        new String[]{ "name", "age" }, 
                        "age = ?", new String[]{ "" + age }, 
                        null, null, null );
 
                // 検索結果をcursorから読み込んで返す
                return readCursor( cursor );
            }
            finally{
                // Cursorを忘れずにcloseする
                if( cursor != null ){
                    cursor.close();
                }
            }
        }
 
 
        /** 検索結果の読み込み */
        private String readCursor( Cursor cursor ){
            String result = "";
 
            // まず、Cursorからnameカラムとageカラムを
            // 取り出すためのインデクス値を確認しておく
            int indexName = cursor.getColumnIndex( "name" );
            int indexAge  = cursor.getColumnIndex( "age"  );
 
            // ↓のようにすると、検索結果の件数分だけ繰り返される
            while( cursor.moveToNext() ){
                // 検索結果をCursorから取り出す
                String name = cursor.getString( indexName );
                int    age  = cursor.getInt   ( indexAge  );
                result += name + " さん(" + age + " 歳)\n";
            }
            return result;
        }


 query()メソッドを使って検索する部分と、検索結果をCursorオブジェクトから取り出す部分に分けてみた。まず、query()メソッドで検索する部分(上記サンプルのsearchByAge()メソッド)について説明しよう。

 query()メソッドにはたくさんの引数があるが、この例では必要最低限のものだけを指定している。第1引数には検索対象のテーブル名を指定する(省略不可)。第2引数には取得するカラム名を指定する。これはnullを与えて省略できる(その場合、テーブルの全カラムが取得されることになる)。

 ややこしいのが第3、第4引数だ。第3引数には、検索条件を記述する。そして、第4引数で検索条件に埋め込むパラメータをString型の配列で指定する。第3引数の検索条件は、SQLのWhere句に当たり、この例では“age=?”としている。この“?”と書いている部分が、第4引数で指定した文字列に置き換わる。

 検索条件には、複数の埋め込みパラメータを設定できる。例えば、名前と年齢の両方が一致するデータを検索したい場合は以下のようになる。

  • 第3引数:"name=? and age=?"
  • 第4引数:new String[]{ name, age }

 query()の残りの引数は、SQLのgroup by句、having句、order by句などに当たる。今回は取りあえずnullを指定しておく。

 次に、Cursorオブジェクトから検索結果を取り出す処理(上記サンプルのreadCursor()メソッド)について説明する。

 ……とはいえ、なかなか言葉では説明しづらいので上記のサンプルを参照してほしい。

 Cursorオブジェクトには、検索された件数分のデータが格納されており、moveToNext()メソッドで1件ずつデータを読み進めては、結果を取り出していく。そして、「これ以上格納されているデータがない」という状態まで読み込みを進めると、moveToNext()がfalseを返すため、ここで読み込み処理は終了となる。while()句の中で、いきなりmoveToNext()を実行しているので、最初の1件を読み飛ばしているように見えるが、実は検索直後は「0件目」を指した状態になっているので、上記の処理で問題ない。

削除

 次に、データの削除について説明する。削除処理の例は以下の通りだ。

        /** 年齢を条件に削除 */
        private void delByAge( SQLiteDatabase db, int age ){
            db.delete( "name_book_table", 
                    "age = ?",
                    new String[]{ "" + age } );
        }
 
 
        /** 無条件で削除(全削除) */
        private void delAllEntry( SQLiteDatabase db ){
            db.delete( "name_book_table", null, null );
        }


 削除処理には、SQLiteDatabaseのdelete()メソッドを使用する。第1引数は削除対象のテーブル名、第2、第3引数は削除条件で、query()メソッドの検索条件と同じ形式で指定する。検索条件はnullで省略できる。その場合、テーブル上の全データが削除対象となる。delete()メソッドの結果は、実際に削除された件数がlong型で返される。

更新

 最後に、データの更新についてだ。実装例は以下のようになる。

        /** 特定の年齢のデータを更新 */
        private void updateEntry( SQLiteDatabase db, int targetAge, String newName, int newAge ){
            // 更新内容はContentValuesに格納しておく
            ContentValues val = new ContentValues();
            val.put( "name", newName );
            val.put( "age" , newAge  );
 
            // 更新するデータの条件はquery()やdelete()と同じように記述する
            db.update( "name_book_table", 
                    val,
                    "age = ?",
                    new String[]{ "" + targetAge } );
        }


 データの更新は、SQLiteDatabaseのupdate()メソッドを使用する。第1引数はやはりテーブル名を指定する。第2引数はデータの更新内容で、insert()のときと同じようにContentValuesに格納した状態で受け渡す。第3、第4引数は「どのデータを更新する」という条件の指定で、query()やdelete()のときと同じように指定する。update()メソッドの結果は、実際に更新された件数がlong型で返される。






query

public ArrayList LoradMemberHisData(SQLiteDatabase database, Integer memberID) {
ArrayList data = new ArrayList<>();
String query = "select a.MemberID, b._id,b.Type,a.Amout, a.ReasonDesc, a.GetTime from MoneyHis a , Reason b "+
"where a.MemberID="+memberID+" order by a.GetTime desc";


Cursor c = database.rawQuery(query, null);
c.moveToFirst();
int i = 0;
while (!c.isAfterLast()) {
try {
String Type=c.getString(c.getColumnIndex("Type"));
String id=c.getString(c.getColumnIndex("_id"));
String dMemberID=c.getString(c.getColumnIndex("MemberID"));
String amout=c.getString(c.getColumnIndex("Amout"));
String ReasonDesc=c.getString(c.getColumnIndex("ReasonDesc"));
String getTime=c.getString(c.getColumnIndex("GetTime"));
data.add(dMemberID+" "+id+" "+Type+" "+ReasonDesc+" "+amout+""+" "+getTime);


//Log("Name=" + c.getString(c.getColumnIndex("Name")));
c.moveToNext();
i++;
} catch (Exception e) {
e.printStackTrace();
}
}
c.close();
return data;
}





from https://mrraybox.blogspot.com/2017/01/android-sqlite-onupgrade.html

【教學】Android SQLite onUpgrade方法的運用

本篇主要是示範:
1. 說明onUpgrade何時會啟用。
2. 示範如何使用onUpgrade。
3. 觀察onUpgrade使用後的前後差異。

相信開始接觸SQLite的大家都知道SQLiteOpenHelper中有一個方法叫onUpgrade,但是這個方法是做什麼用的呢?首先我們知道onCreate是在Android載入時,找不到對應的資料庫資料,就會觸發的一個方法。而onUpgrade呢,則是在資料庫的結構有所改變時,才會觸發的一個方法。

舉例來說,假如我在一月時,寫了一個App給其他人使用,在二月時,因改版需求,所以需要新增或刪減資料表的欄位。但是此時,那些使用我們所寫的App的用戶們,在這段期間已經儲存許多資料了。為了保存用戶舊的資料,此時我們就需要運用到onUpgrade來修改我們資料表的結構。

首先這裡示範我們一開始所寫的App。首先Layout部分的配置。
File Name:activity_main.xml
<ScrollView 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" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <Button
                android:id="@+id/btn_new"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="新增" />

            <Button
                android:id="@+id/btn_delete"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="刪除" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:orientation="horizontal" >

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="ID"
                android:id="@+id/edt1"
                android:layout_weight="1" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="Row1"
                android:id="@+id/edt2"
                android:layout_weight="1" />

            <EditText
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:hint="Row2"
                android:id="@+id/edt3"
                android:layout_weight="1" />

            <Button
                android:id="@+id/btn_alter"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="修改" />


        </LinearLayout>

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

    </LinearLayout>

</ScrollView>

File Name:MainActivity
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnNew, btnDel, btnAlt;
    private EditText edtID, edtRow1, edtRow2;
    private TextView txt;

    private MyHelper helper;
    private SQLiteDatabase db;
    private Cursor c;

    private String result;

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

        btnNew = (Button) this.findViewById(R.id.btn_new);
        btnDel = (Button) this.findViewById(R.id.btn_delete);
        btnAlt = (Button) this.findViewById(R.id.btn_alter);
        edtID = (EditText) this.findViewById(R.id.edt1);
        edtRow1 = (EditText) this.findViewById(R.id.edt2);
        edtRow2 = (EditText) this.findViewById(R.id.edt3);
        txt = (TextView) this.findViewById(R.id.txt);

        btnNew.setOnClickListener(this);
        btnDel.setOnClickListener(this);
        btnAlt.setOnClickListener(this);

        helper = new MyHelper(this, "dbname", null, 1);
        db = helper.getWritableDatabase();

        queryDB();
    }

    private void queryDB() {
        c = db.query("tablename", null, null, null, null, null, null);

        result="";
        for(int i=0; i<c.getColumnCount();i++){
            result+=c.getColumnName(i)+", ";
        }
        result+="\n";

        while (c.moveToNext()) {
            result += c.getString(c.getColumnIndex("_id")) + ", "+
                    c.getString(c.getColumnIndex("row1")) + ", "+
                    c.getString(c.getColumnIndex("row2")) +"\n";
        }
        txt.setText(result);
    }


    @Override
    public void onClick(View view) {
        String id = edtID.getText().toString();
        ContentValues values = new ContentValues();
        switch (view.getId()){
            case R.id.btn_new:
                values.put("row1", "Row1");
                values.put("row2", "Row2");
                db.insert("tablename", null, values);
                queryDB();
                break;
            case R.id.btn_delete:
                db.delete("tablename", "_id="+id,null);
                queryDB();
                break;
            case R.id.btn_alter:
                String row1 = edtRow1.getText().toString();
                String row2 = edtRow2.getText().toString();
                values.put("row1", row1);
                values.put("row2", row2);
                db.update("tablename", values, "_id=" + id, null);
                queryDB();
                break;
        }
    }

    @Override
    public void finish() {
        c.close();
        db.close();
        super.finish();
    }
}

File Name:MyHelper
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyHelper extends SQLiteOpenHelper {

    private String createTab;
    private String tableName = "tablename";

    public MyHelper(Context context, String db_name, SQLiteDatabase.CursorFactory factory, int db_version) {
        super(context, db_name, factory, db_version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        createTab = "create table "+ tableName +" (_id integer primary key AUTOINCREMENT, row1 text, row2 text)";
        db.execSQL(createTab);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

在用戶新增完資料後,就會變成下面這樣。


接著是我們改版後的程式碼。
File Name:MainActivity
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button btnNew, btnDel, btnAlt;
    private EditText edtID, edtRow1, edtRow2;
    private TextView txt;

    private MyHelper helper;
    private SQLiteDatabase db;
    private Cursor c;

    private String result;

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

        btnNew = (Button) this.findViewById(R.id.btn_new);
        btnDel = (Button) this.findViewById(R.id.btn_delete);
        btnAlt = (Button) this.findViewById(R.id.btn_alter);
        edtID = (EditText) this.findViewById(R.id.edt1);
        edtRow1 = (EditText) this.findViewById(R.id.edt2);
        edtRow2 = (EditText) this.findViewById(R.id.edt3);
        txt = (TextView) this.findViewById(R.id.txt);

        btnNew.setOnClickListener(this);
        btnDel.setOnClickListener(this);
        btnAlt.setOnClickListener(this);

        helper = new MyHelper(this, "dbname", null, 2);
        db = helper.getWritableDatabase();

        queryDB();
    }

    private void queryDB() {
        c = db.query("tablename", null, null, null, null, null, null);

        result="";
        for(int i=0; i<c.getColumnCount();i++){
            result+=c.getColumnName(i)+", ";
        }
        result+="\n";

        while (c.moveToNext()) {
            result += c.getString(c.getColumnIndex("_id")) + ", "+
                    c.getString(c.getColumnIndex("row1")) + ", "+
                    c.getString(c.getColumnIndex("row2")) +"\n";
        }
        txt.setText(result);
    }


    @Override
    public void onClick(View view) {
        String id = edtID.getText().toString();
        ContentValues values = new ContentValues();
        switch (view.getId()){
            case R.id.btn_new:
                values.put("row1", "Row1");
                values.put("row2", "Row2");
                db.insert("tablename", null, values);
                queryDB();
                break;
            case R.id.btn_delete:
                db.delete("tablename", "_id="+id,null);
                queryDB();
                break;
            case R.id.btn_alter:
                String row1 = edtRow1.getText().toString();
                String row2 = edtRow2.getText().toString();
                values.put("row1", row1);
                values.put("row2", row2);
                db.update("tablename", values, "_id=" + id, null);
                queryDB();
                break;
        }
    }

    @Override
    public void finish() {
        c.close();
        db.close();
        super.finish();
    }
}

File Name:MyHelper
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyHelper extends SQLiteOpenHelper {

    private String createTab;
    private String tableName = "tablename";

    public MyHelper(Context context, String db_name, SQLiteDatabase.CursorFactory factory, int db_version) {
        super(context, db_name, factory, db_version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        createTab = "create table "+ tableName +
                " (_id integer primary key AUTOINCREMENT, row1 text, row2 text, row3 text)";

        db.execSQL(createTab);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        if(oldVersion == 1 && newVersion == 2){
            //Adding a row3
            String sql = "alter table "+ tableName +" add row3 text";
            db.execSQL(sql);
        }

    }
}

實作後,則會變成下面這樣。有發現差異在哪嗎?


改版後的App不但保存了用戶的舊資料,同時還新增了一個row3的欄位,是不是很方便的一個功能呀!



留言

熱門文章