gist

2012年1月23日月曜日

java.lang.IllegalStateException: database not open

SQLiteOpenHelperのonCreate,onUpgradeメソッド内でSQLiteDatabaseオブジェクトをcloseすると、IllegalStateException: database not open の例外で落ちることがあります。

SQLiteOpenHelperを継承したIllegalStateExceptionの例

package com.luckyandhappy;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class SampleDatabaseHelper extends SQLiteOpenHelper {
 
 private static final String TAG = SampleDatabaseHelper.class.getSimpleName();

 private static final String DB_NAME = "Sample.db";
 private static final int DB_VERSION = 1;

 public SampleDatabaseHelper(Context context) {
  super(context, DB_NAME, null, DB_VERSION);
 }

 @Override
 public void onCreate(SQLiteDatabase db) {
  db.beginTransaction();
  try {
   db.execSQL("CREATE TABLE `persons` (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
  }
  catch(Exception e) {
   Log.e(TAG,"スキーマ作成中に例外が発生しました",e);
   db.setTransactionSuccessful();
  }
  finally {
   db.endTransaction();
   ///// !!!!! /////
   ///// ここでcloseするとSQLiteDatabaseオブジェクトの状態が
   ///// 変わって他のメソッドに影響します!
   db.close();
  }
 }

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

 }
 
 public List<Person> findAllPersons() {
  List<Person> persons = new ArrayList();
  SQLiteDatabase db = getWritableDatabase();
  Cursor cursor = db.query("persons", new String[]{"id","name"}, null, null, null, null, null);
  while(cursor.moveToNext()) {
   Person person = new Person();
   person.setId(cursor.getInt(0));
   person.setName(cursor.getString(1));
   persons.add(person);
  }
  db.close();
  
  return persons;
 }
 
 public void save(Person person) {
  SQLiteDatabase db = getWritableDatabase();
  db.beginTransaction();
  try {
   db.execSQL("INSERT INTO `persons` (name) VALUES (?)", new String[]{person.getName()});
   db.setTransactionSuccessful();
  } catch (Exception e) {
   Log.e(TAG, "保存中に例外が発生しました",e);
  } finally {
   db.endTransaction();
   db.close();
  }
 }

 public void deleteAllPersons() {
  SQLiteDatabase db = getWritableDatabase();
  db.beginTransaction();
  try {
   db.execSQL("DELETE FROM `persons`");
   db.setTransactionSuccessful();
  } catch (Exception e) {
   Log.e(TAG, "削除中に例外が発生しました",e);
  } finally {
   db.endTransaction();
   db.close();
  }
 }

}

例外

Caused by: java.lang.IllegalStateException: database not open
  at android.database.sqlite.SQLiteDatabase.endTransaction(SQLiteDatabase.java:555)
  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:137)
  at com.luckyandhappy.SampleDatabaseHelper.deleteAllPersons(SampleDatabaseHelper.java:74)
  at com.luckyandhappy.SQLiteOnCreateExampleActivity.onCreate(SQLiteOnCreateExampleActivity.java:18)
  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)
  ... 11 more

データベースが存在しない場合に、一番最初のsave、deleteAllPersons, findAllPersons内でgetWritabeDatabaseが呼ばれると、onCreateが呼ばれます。一連のOpen-Closeの中でonCreateが実行されますので、勝手にSQLiteOpenHelperをcloseすると後のクエリが正しく実行されません。

なのでonCreateを以下のように修正します。

 @Override
 public void onCreate(SQLiteDatabase db) {
  db.execSQL("CREATE TABLE `persons` (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL)");
 }

同様の理由で、どこかでopenしたまま、closeして、更にクエリを流すとこのIlligalStateExceptionが発生します。Open/Closeをイチイチ記述するのがベターです。SQLiteDatabaseオブジェクトのisOpenメソッドでチェックしてからgetWritableDatabaseを使うともう少し良いかもです。

わかれば簡単ですが地味にハマります。

0 件のコメント: