umegusa's blog

備忘録

SQLiteを使ってみた

家計簿作るためにいろいろ試行錯誤してたけど、全く分からないところが多くあったのでその勉強。
とりあえずAndroidで作ろうと思っています。

UIは取り合えず後回しにして、データの永続化について調べました。
まずはデータベースを保持するためにデータベースの扱いについて学んだことをメモ。

SQLite

そもそもSQLiteってなんぞって話です。
SQLiteオープンソースのデータベースで、クライアントサーバ方式をとらず、軽量という特徴があります。また、SQLの機能をサポートしているみたいです。

クライアントサーバ方式をとらないので、サーバ名やポート番号を指定する必要はないです。
アプリケーションごとにデータベースを持たせて、アプリケーション側のプログラミングでデータベース操作の制限を行うようです。

また、サポートされているデータ型が少ないです。
NULL, INTEGER, REAL, TEXT, BLOBだけのようですね。
制限はされているものの、あまりデータ型を意識しないで使用することができそうです。

SQLiteを使ってDB操作

ソースコードと一緒に解説します。

まずはデータベースを作成・削除するためのHelperクラスを作成します。
HelperクラスはSQLiteOpenHelperクラスを継承します。
DatabaseHelper.java

package com.example.app;

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

public class DatabaseHelper extends SQLiteOpenHelper{

    private static final String DATABASE_NAME = "db_data";
    private static final int DATABASE_VERSION = 1;

    // テーブル作成SQL
    private static final String CREATE_SCHEDULE_TABLE_SQL = "create table data"
            + "(id integer primary key autoincrement,"
            + "memo text not null, "
            + "priority integer default 1)";

    // テーブル削除SQL
    private static final String DROP_SCHEDULE_TABLE_SQL = "drop table if exists data";

    public DatabaseHelper(Context context){
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // テーブル作成
    // 必須
    @Override
    public void onCreate(SQLiteDatabase db){
        db.execSQL(CREATE_SCHEDULE_TABLE_SQL);
    }

    // テーブルの再作成
    // 必須
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
        db.execSQL(DROP_SCHEDULE_TABLE_SQL);
        onCreate(db);
    }

}


テーブル作成用のSQL文を定義して、onCreateメソッドでテーブルを作成する処理を実行しています。
onUpgradeメソッドでは古いテーブルを削除し、新しいテーブルを作成しています。


続いて、データベースへのデータ挿入、更新操作を行うためのDAOクラスを定義します。
DAOクラスはデザインパターンの一種DAOパターンと同じ考えで、データアクセスをビジネスロジックから排除し、データアクセスオブジェクトとしてカプセル化することが目的です。
DatabaseDao.java

package com.example.app;

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

import java.net.ContentHandler;
import java.util.ArrayList;
import java.util.List;

public class DatabaseDao {
    private static final String TABLE_NAME = "data";
    private static final String COLUMN_ID = "id";
    private static final String COLUMN_MEMO = "memo";
    private static final String COLUMN_PRIORITY = "priority";
    private static final String[] COLUMNS =
            {COLUMN_ID, COLUMN_MEMO, COLUMN_PRIORITY};

    private SQLiteDatabase db;

    public DatabaseDao(SQLiteDatabase db){
        this.db = db;
    }

    public long insert(DBData dbData){
        ContentValues values = new ContentValues();
        values.put(COLUMN_MEMO, dbData.getMemo());
        values.put(COLUMN_PRIORITY, dbData.getPriority());
        return db.insert(TABLE_NAME, null, values);
    }

    public int update(DBData dbData){
        ContentValues values = new ContentValues();
        values.put(COLUMN_ID, dbData.getId());
        values.put(COLUMN_MEMO, dbData.getMemo());
        values.put(COLUMN_PRIORITY, dbData.getPriority());
        String whereClause = "id = " + dbData.getId();
        return db.update(TABLE_NAME, values, whereClause, null);
    }

    public List<DBData> findAll(){
        List<DBData> dbDataList = new ArrayList<DBData>();
        Cursor cursor =
                db.query(TABLE_NAME, COLUMNS, null, null, null, null, COLUMN_ID);
        while(cursor.moveToNext()){
            DBData dbData = new DBData();
            dbData.setId(cursor.getInt(0));
            dbData.setMemo(cursor.getString(1));
            dbData.setPriority(cursor.getInt(2));
            dbDataList.add(dbData);
        }
        return dbDataList;
    }

    public DBData findById(int id){
        String selection = "id = " + id;
        Cursor cursor =
                db.query(TABLE_NAME, COLUMNS, selection, null, null, null, null);
        while(cursor.moveToNext()){
            DBData dbData = new DBData();
            dbData.setId(cursor.getInt(0));
            dbData.setMemo(cursor.getString(1));
            dbData.setPriority(cursor.getInt(2));
            return dbData;
        }
        return null;
    }

    public int delete(int id){
        return db.delete(TABLE_NAME, "id = " + id, null);
    }

}

SQLiteDatabese#queryメソッドでSQL文を指定してデータベースを検索し、検索結果をCursorインターフェースで取得するというような処理です。

ContentValuesクラスは、列名と値を1組にしてデータを保持するためのクラスで、CursorインターフェースはSQLiteDatabase#queryメソッドから戻ってくる検索結果カーソルのインターフェースです。
ContentValuesクラスはSQLiteDatabaseクラスのisnert, updateメソッドを利用し、データベースへのデータの挿入・更新をする際に利用します。
カーソルは検索結果の1レコードに対応し、検索結果の次のレコードに移動させたり、レコードから値を取り出したりする検索結果へのアクセス手段を提供します。

データベースの検索結果を格納するDBDataクラスは以下のようになります。

DBData.java

package com.example.app;

public class DBData {
    private int id;
    private String memo;
    private int priority;

    public int getId(){
        return id;
    }

    public void setId(int id){
        this.id = id;
    }

    public String getMemo(){
        return memo;
    }

    public void setMemo(String memo){
        this.memo = memo;
    }

    public int getPriority(){
        return priority;
    }

    public void setPriority(int priority){
        this.priority = priority;
    }

}

最後にデータベースにデータを格納し、検索結果を表示するActivityクラスを実装します。

MainActivity.java

package com.example.app;

import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
import android.widget.TextView;

import java.util.List;

public class MainActivity extends ActionBarActivity {

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

        DatabaseHelper dbHelper = new DatabaseHelper(this);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        DatabaseDao databaseDao = new DatabaseDao(db);


        DBData newDBData = new DBData();
        newDBData.setMemo("hogehoge");
        newDBData.setPriority(1);
        databaseDao.insert(newDBData);

        newDBData = new DBData();
        newDBData.setMemo("fugafuga");
        newDBData.setPriority(3);
        databaseDao.insert(newDBData);

        List<DBData> dbDataList = databaseDao.findAll();
        db.close();

        StringBuffer buf = new StringBuffer();
        buf.append("     Date     | Plan");
        buf.append(System.getProperty("line.separator"));
        for(DBData dbData: dbDataList){
            buf.append(dbData.getMemo());
            buf.append("|");
            buf.append(dbData.getPriority());
            buf.append(System.getProperty("line.separator"));
        }
        TextView tv = new TextView(this);
        tv.setText(buf.toString());
        setContentView(tv);

    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.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();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }
    }

}

最後に

SQLiteOpenHelperを継承したHelperクラス、データベース操作をまとめたDAOクラスを使用してデータベース操作を行うのが主流みたいですね。

これでとりあえずデータベース操作については大丈夫かなと思います。
他にも必要そうなものがあれば調べてみようと思います。
DB設計とかもしたほうがいいかもしれないです。

それでは。