Androidアプリでデータベースを扱う方法【Java】
SQLiteOpenHelperを利用する
概要
Androidアプリ開発でデータベースを扱うときの方法をまとめます。
SQLiteOpenHelperを継承したクラスを用意することで簡単にデータベースの実装ができます。また、SQLiteOpenHelperを経由して作成されるデータベースはMODE_PRIVATE固定となっており、他のアプリからのアクセスが許されないので安全です。
今回は自作するSampleDatabaseHelperクラスにSQLiteOpenHelperを継承させて、sampleという名前のデータベースを用意し、tableAとtableBの2つのテーブルを作成します。
テーブルA
_id(int) | name(String) | weight(double) | height(double) |
---|
テーブルB
_id(int) | name(String) | comment(String) |
---|
このデータベースを例にデータの入出力の方法をまとめます。
SQLiteOpenHelperを継承したクラスを作る
以下のようにSampleDatabaseHelperクラスを作成します。
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.appcompat.app.AppCompatActivity;
public class SampleDatabaseHelper extends SQLiteOpenHelper{
//データベース名とバージョン
private static final String DATABASE_NAME = "sample";
private static final int DATABASE_VERSION = 1;
//テーブル名
public static final String TABLE_NAME_A = "tableA";
public static final String TABLE_NAME_B = "tableB";
//カラム名
public static final String COLUMN_ID = "_id";
public static final String COLUMN_NAME = "name";
public static final String COLUMN_WEIGHT = "weight";
public static final String COLUMN_HEIGHT = "height";
public static final String COLUMN_COMMENT = "comment";
//コンストラクター
public SampleDatabaseHelper(AppCompatActivity context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db){
//テーブルの作成
String tableA_sql = "CREATE TABLE "+ TABLE_NAME_A+ " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY,"
+ COLUMN_NAME + " TEXT,"
+ COLUMN_WEIGHT + " DOUBLE,"
+ COLUMN_HEIGHT + " DOUBLE"
+ ")";
String tableB_sql = "CREATE TABLE "+ TABLE_NAME_B+ " ("
+ COLUMN_ID + " INTEGER PRIMARY KEY,"
+ COLUMN_NAME + " TEXT,"
+ COLUMN_COMMENT + " TEXT"
+ ")";
db.execSQL(tableA_sql);
db.execSQL(tableB_sql);
}
@Override
public void onUpgrade(SQLiteDatabase db,int i,int i1){}
}
データベース本体の実装はこれだけで終わりです。
コンストラクターで親クラスSQLiteOpenHelperに渡す引数は4つです。
- コンテキスト
- データベース名
- カーソルに加える特別な処理
- データベースのバージョン
※カーソルに加える特別な処理とは、データを受け取るときに使用するカーソルを拡張して特別な処理を追加したい場合に渡すものです。nullにすればデフォルトのままで利用します。
このうちデータベース名とデータベースのバージョンは自作するSampleDatabaseHelperクラスの中で定義して固定してしまいます。そして、カーソルに加える特別な処理は通常nullで問題ありません。したがって、データベースにアクセスするときにSampleDatabaseHelperの引数として渡すものはコンテキストのみとなります。
次に、onCreateメソッドでテーブルの作成を行うSQLを実行しています。
CREATE TABLE テーブル名 (カラム名1 INTEGER PRIMARY KEY, カラム名2 TEXT, カラム名3 DOUBLE, カラム名4 DOUBLE)
_idはintデータを扱うのでINTEGER、nameはStringデータを扱うのでTEXT、weightとheightはdoubleデータを扱うのでDOUBLEとしています。そして、_idはデータの特定に使いたいのでPRIMARY KEYを設定してID番号の重複を許さないようにしています。こうしておくとデータを追加したときに自動的に番号が振られます。
上記のようなSQL文を作成したらexecSQL(SQL文(String))でSQLを実行してテーブルを作成します。このonCreateメソッドはデータベースにアクセスするたびに実行されますが、同じテーブルが重複したり、すでに存在しているテーブルが上書きされたりすることはありません。
最後に、onUpgradeメソッドはすでに存在しているデータベースとこれからアクセスしようとしているデータベースのバージョンが異なる場合に呼ばれるもので、データベースのカラムを変更したりする場合に使用するものです。SampleDatabaseHelper内で定義しているDATABASE_VERSIONの値を変更することでonUpgradeが呼ばれます。今回はそこまで想定していないので触れません。
データベースにアクセスする準備
データベースにアクセスするActivity側では以下のようになります。
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//SampleDatabaseHelperのインスタンス
SampleDatabaseHelper helper = new SampleDatabaseHelper(this);
}
}
SampleDatabaseHelperにthis(Activityのコンテキスト)を渡してインスタンス化します。
これでデータベースへのアクセス準備は完了です。
データを追加する:[insert]
データを追加する場合はinsertを使います。
insertメソッドの3つの引数
- データを追加するテーブル名
- nullを設定できるカラム名(nullColumnHack)
- ContentValues(データ)
※nullを設定できるカラム名(nullColumnHack)とは、ContentValuesが空である場合に通常エラーとなるところを代わりに指定したカラムにnullを入れることで処理させることができるというものです。指定したカラムがnullを許容できない場合はやはりエラーになります。ContentValuesが空になることがない場合は第二引数にnullを入れておいて問題ありません。
自作したSampleDatabaseHelperが継承しているSQLiteOpenHelperのgetWritableDatabase()メソッドを使うことで書き込み/読み取りが可能なモードでデータベースを開きます。
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//SampleDatabaseHelperのインスタンス
SampleDatabaseHelper helper = new SampleDatabaseHelper(this);
//データを追加する場合
try(SQLiteDatabase db = helper.getWritableDatabase()){
ContentValues cv = new ContentValues();
cv.put(SampleDatabaseHelper.COLUMN_NAME, "Ethan");
cv.put(SampleDatabaseHelper.COLUMN_HEIGHT, 170.0);
cv.put(SampleDatabaseHelper.COLUMN_WEIGHT, 60.0);
db.insert(SampleDatabaseHelper.TABLE_NAME_A, null, cv);
}
}
}
ContentValuesにputメソッドで(カラム名, 値)を3つ入れています。
SQLiteDatabaseでデータを追加するinsertメソッドには、このContentValuesを渡します。
_idは自動で振られます。
これでデータの追加は完了です。
データを取り出す:[query]
データを取り出す場合はqueryメソッドを使います。
queryメソッドの8つの引数
- データを取得するテーブル名
- データを取得するカラム名の配列
- データを検索する条件
- データを検索する条件にバインドする値の配列
- グループ化するカラム(groupBy)
- グループ化後のデータ検索条件(having)
- データをソートするカラム(orderBy)
- 取得するデータ数の制限(limit)
※テーブル名以外を全てnullにすればテーブル内の全てのデータを取得することができます。
自作したSampleDatabaseHelperが継承しているSQLiteOpenHelperのgetReadableDatabase()メソッドを使うことで読み取り専用モードでデータベースを開きます。
import androidx.appcompat.app.AppCompatActivity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//SampleDatabaseHelperのインスタンス
SampleDatabaseHelper helper = new SampleDatabaseHelper(this);
//データを取り出す場合
try(SQLiteDatabase db = helper.getReadableDatabase()){
Cursor data_list = db.query(SampleDatabaseHelper.TABLE_NAME_A,
null,
null,
null,
null,
null,
null,
null);
int id_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_ID);
int name_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_NAME);
int weight_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_WEIGHT);
int height_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_HEIGHT);
boolean exist_data = data_list.moveToFirst();
while(exist_data){
if(id_index >= 0){
int id = data_list.getInt(id_index);
}
if(name_index >= 0){
String name = data_list.getString(name_index);
}
if(weight_index >= 0){
double weight = data_list.getDouble(weight_index);
}
if(height_index >= 0){
double height = data_list.getDouble(height_index);
}
exist_data = data_list.moveToNext();
}
data_list.close();
}
}
}
上記の例では全てのデータを取得するように全てnullを指定していますが、参考例として何かしらの条件をつける場合の書き方は以下のようになります。(内容は適当)
Cursor data_list = db.query("tableA", //テーブル名
new String[]{"_id", "name", "avg(weight)"}, //データを取得するカラム
"_id <= ? and weight > ?", //データを検索する条件
new String[]{"4", "50.0"}, //データを検索する条件にバインドする値
"height", //グループ化するカラム
"avg(weight) > 55.0", //グループ化後のデータ検索条件
"weight ASC", //データをソートするカラム(昇順)
"3"); //取得するデータ数の制限
※avg(weight)のようにすると、weightの値の平均値を出すことができます。
ただし、queryメソッドからそのままデータを取得できるというわけではありません。queryメソッドではCursor(カーソル)というオブジェクトが返ります。
カーソルを取得した後はCursorのmoveToFirst()メソッドにより、カーソル位置を一番上のデータに持っていきます。カーソル位置にデータが存在しなければfalseが返るので、それを利用してmoveToNext()メソッドで一行ずつカーソルを動かしながらwhile文で順番にデータを取り出していきます。
取得対象として指定したカラムには0からのインデックス番号が振られています。全てのデータを取得すると指定した場合には、カラムの順番に_id(0)、name(1)、height(2)、weight(3)と振られていますが、nameとweightだけを指定したような場合はname(0)、weight(1)というようになります。したがって、getColumnIndex(カラム名(String))メソッドを用いてインデックス番号を取得したのち、取得したいデータ型に応じてgetInt(インデックス番号)、getString(インデックス番号)、getDouble(インデックス番号)でデータを取得します。
int id_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_ID);
int name_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_NAME);
int weight_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_WEIGHT);
int height_index = data_list.getColumnIndex(SampleDatabaseHelper.COLUMN_HEIGHT);
boolean exist_data = data_list.moveToFirst();
while(exist_data){
if(id_index >= 0){
int id = data_list.getInt(id_index);
}
if(name_index >= 0){
String name = data_list.getString(name_index);
}
if(weight_index >= 0){
double weight = data_list.getDouble(weight_index);
}
if(height_index >= 0){
double height = data_list.getDouble(height_index);
}
exist_data = data_list.moveToNext();
}
※getColumnIndexはカラムが存在しない場合には-1を返すので、-1のインデックス番号を許容できないgetInt、getString、getDoubleのためにインデックス番号が0以上であることを確認する必要があります。
Cursorは最後にclose()を呼び出してカーソルを閉じるようにします。本来であればSQLiteDatabaseもclose()で閉じる必要がありますが、この場合はtry-with-resources文で書いているのでclose()は必要ありません。
データを更新する:[update]
データを更新する場合updateメソッドを使います。
updateメソッドの4つの引数
- データを更新するテーブル名
- ContentValues(データ)
- データを検索する条件
- データを検索する条件にバインドする値の配列
自作したSampleDatabaseHelperが継承しているSQLiteOpenHelperのgetWritableDatabase()メソッドを使うことで書き込み/読み取りが可能なモードでデータベースを開きます。
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//SampleDatabaseHelperのインスタンス
SampleDatabaseHelper helper = new SampleDatabaseHelper(this);
//データを更新する場合
try(SQLiteDatabase db = helper.getWritableDatabase()){
ContentValues cv = new ContentValues();
cv.put(SampleDatabaseHelper.COLUMN_NAME, "Ethan2");
db.update(SampleDatabaseHelper.TABLE_NAME_A, cv, SampleDatabaseHelper.COLUMN_NAME+ " = ?", new String[]{"Ethan"});
}
}
}
insertのときと同じようにContentValuesに(カラム名,値)をセットして渡します。更新する必要があるカラムだけをContentValuesにセットします。
上記の例だと、nameがEthanであるデータ全てを対象として、nameをEthan2に書き換えます。
データを削除する:[delete]
データを削除する場合はdeleteメソッドを使います。
deleteメソッドの3つの引数
- データを削除するテーブル名
- データを検索する条件
- データを検索する条件にバインドする値の配列
※条件にnullを指定すれば全てのデータを削除します。
自作したSampleDatabaseHelperが継承しているSQLiteOpenHelperのgetWritableDatabase()メソッドを使うことで書き込み/読み取りが可能なモードでデータベースを開きます。
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//SampleDatabaseHelperのインスタンス
SampleDatabaseHelper helper = new SampleDatabaseHelper(this);
try(SQLiteDatabase db = helper.getWritableDatabase()){
db.delete(SampleDatabaseHelper.TABLE_NAME_A,
null,
null);
}
}
}
上記の例では条件にnullを指定して全てのデータを削除しています。
条件を指定すれば条件にあてはまったデータが全て削除されます。
投稿されたコメント一覧