【AndroidでSQLiteを制する】SQLiteDatabase#insertを使用してデータをインサートする

このエントリーを Google ブックマーク に追加
Pocket
[`yahoo` not found]

SQLデータベース内のテーブルにデータを挿入します。
今回はSQLiteDatabaseに定義されているインサートを行うコンビニエンスメソッドを使用します。

SQLiteを使用するActivityを実装する

今回の一連の記事で使用するActivityを実装します。
  public class SQLiteActivity extends AppCompatActivity {
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
      }
  }

DTOをContentValuesに変換するメソッドを実装する

テーブルのデータを挿入、更新する場合はContentValuesを使用します。
そこで、BookDTOをContentValuesに変換するメソッドを定義します。
カラムにnullを設定する場合はカラム名を入れないようにしましょう。
BookDAO
    public static ContentValues convertContentValues(BookDTO bookDTO) {
      ContentValues contentValues = new ContentValues();
      if(bookDTO.getId() != 0) {
          contentValues.put(LoadToSQLiteMasterContract.Book._ID, bookDTO.getId());
      }
      if(bookDTO.getTitle() != null) {
          contentValues.put(LoadToSQLiteMasterContract.Book.COLUMN_NAME_TITLE, bookDTO.getTitle());
      }
      if(bookDTO.getSubtitle() != null) {
          contentValues.put(LoadToSQLiteMasterContract.Book.COLUMN_NAME_SUBTITLE, bookDTO.getSubtitle());
      }
      return contentValues;
    }

SQLiteDatabase#insertを使用する

SQLiteDatabase#insertはデータベース内のテーブルにデータを挿入するコンビニエンスメソッドです。
String table データベース内のテーブル名を指定します。
String nullColumnHack SQLでは完全に空の行を挿入することは許されていません。 この引数は第3引数に空のContentValuesを指定された場合に意味を持ちます。 このメソッドはContentValuesが空の場合はインサート文を作成することができないため、この引数に適当なカラム名を指定します。 下記のようなSQLが生成されます。
INSERT INTO [table名] ([nullColumnHack]) VALUES (NULL);
ContentValues values ContentValuesを使用してインサートするデータを設定して渡します。
ContentValues#putの第1引数にテーブルのカラム名を、第2引数にはデータを設定します。
このメソッドはSQLエラーが発生しても例外が発生することがないことに注意してください。 SQLエラーが発生した時にこのメソッドは-1を返却します。
BookDAOにinsertを実装する
また、BookDTOをContentValuesに変換するメソッドを実装します。
    public static long insert(Context context , BookDTO bookDTO){
        SQLiteDatabase sqLiteDatabase = getSqLiteDatabase(context,true);
        long rowId = sqLiteDatabase.insert(
                LoadToSQLiteMasterContract.Book.TABLE_NAME,
                COLUMN_NAME_SUBTITLE,
                convertContentValues(bookDTO)
        );
        return rowId;
    }

SQLiteActivityにBookDAO.insertを使用するinsertBookを実装する。
このinsertBookを二回呼び出すことで制約エラーを発生させます。
しかし、特に例外は発生せず-1が戻ってきます。
  @Override
  protected void onResume() {
      super.onResume();
      insertBook();
      insertBook();
  }

  private void insertBook() {
      BookDTO book = new BookDTO();
      book.setTitle("SQLiteの勉強本");
      book.setSubtitle("サブタイトル : " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()));
      long rowId = BookDAO.insert(getApplicationContext() , book);
      Toast.makeText(this, "rowId:" + rowId , Toast.LENGTH_SHORT).show();
  }

SQLiteDatabase#insertOrThrowBookを使用する

SQLiteDatabase#insertOrThrowBookはデータベース内のテーブルにデータを挿入するコンビニエンスメソッドです。
String table データベース内のテーブル名を指定します。
String nullColumnHack SQLでは完全に空の行を挿入することは許されていません。 この引数は第3引数に空のContentValuesを指定された場合に意味を持ちます。 このメソッドはContentValuesが空の場合はインサート文を作成することができないため、この引数に適当なカラム名を指定します。 下記のようなSQLが生成されます。
INSERT INTO [table名] ([nullColumnHack]) VALUES (NULL);
ContentValues values ContentValuesを使用してインサートするデータを設定して渡します。
ContentValues#putの第1引数にテーブルのカラム名を、第2引数にはデータを設定します。
このメソッドはSQLエラーが発生した場合は例外が発生します。
BookDAOにinsertOrThrowを実装する
    public static long insertOrThrow(Context context , BookDTO bookDTO){
        SQLiteDatabase sqLiteDatabase = getSqLiteDatabase(context,true);
        long rowId = sqLiteDatabase.insertOrThrow(
                LoadToSQLiteMasterContract.Book.TABLE_NAME ,
                COLUMN_NAME_SUBTITLE,
                convertContentValues(bookDTO)
        );
        return rowId;
    }

SQLiteActivityにBookDAO.insertOrThrowを使用するinsertOrThrowBookを実装する。
このinsertOrThrowBookを2回呼び出すことで制約エラーを発生させます。
2回目の呼び出しで一意制約違反が発生し例外が発生します。
  @Override
  protected void onResume() {
      super.onResume();
      insertOrThrowBook();
      // 二回目は制約違反でエラーが発生するので例外終了します。
      insertOrThrowBook();
  }

  private void insertOrThrowBook() {
      BookDTO book = new BookDTO();
      book.setId(1);
      book.setTitle("SQLiteの勉強本");
      book.setSubtitle("サブタイトル : " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()));
      long rowId = BookDAO.insertOrThrow(getApplicationContext() , book);
      Toast.makeText(this, "rowId:" + rowId , Toast.LENGTH_SHORT).show();
  }

SQLiteDatabase#insertWithOnConflictBookを使用する

SQLiteDatabase#insertWithOnConflictBookはデータベース内のテーブルにデータを挿入するコンビニエンスメソッドです。
String table データベース内のテーブル名を指定します。
String nullColumnHack SQLでは完全に空の行を挿入することは許されていません。 この引数は第3引数に空のContentValuesを指定された場合に意味を持ちます。 このメソッドはContentValuesが空の場合はインサート文を作成することができないため、この引数に適当なカラム名を指定します。 下記のようなSQLが生成されます。
INSERT INTO [table名] ([nullColumnHack]) VALUES (NULL);
ContentValues values ContentValuesを使用してインサートするデータを設定して渡します。
ContentValues#putの第1引数にテーブルのカラム名を、第2引数にはデータを設定します。
int conflictAlgorithm SQLiteには制約違反が発生した場合の解消アルゴリズムが複数実装されています。 この引数にはSQLiteDatabaseに定義されています。
CONFLICT_ABORT ON CONFLICT ABORTを表す定数です。 制約違反が発生した時にロールバックが発生しません。 そのため、同じトランザクション内の以前に実行されたSQLによる変更は保持されます。 この設定がデフォルトになっています。
CONFLICT_FAIL ON CONFLICT FAILを表す定数です。 制約違反が発生した時に、コマンドの戻り値としてコード[SQLITE_CONSTRAINT]を返却して打ち切られます。 しかし、制約違反が発生する前に実行されたSQLによる変更は保持され取り消されません。
CONFLICT_IGNORE ON CONFLICT IGNOREを表す定数です。 制約違反が発生した時に、制約違反を含む挿入や変更のコマンドは無視され、後続の処理は継続されます。 この時、エラーは返されません。
CONFLICT_NONE 制約違反時のアクションを指定しない場合に使用します。
CONFLICT_REPLACE ON CONFLICT REPLACEを表す定数です。 ユニーク制約違反が発生した場合は既存の行を削除した後に、挿入または更新が必ず行われます。 NOT NULL制約違反が発生した場合は該当の列にはデフォルト値で更新されます。しかし、列にデフォルト値が設定されていない場合はABORTアルゴリズムが使用されます。 CHECK制約違反が発生した場合はIGNOREアルゴリズムが使用されます。 この競合解消方法は制約を満たすために行を削除するときに行の削除トリガーを起動しません。 この動作は将来のリリースで変更される可能性があります。
CONFLICT_ROLLBACK ON CONFLICT ROLLBACKを表す定数です。 制約違反が発生するとすぐにROLLBACKが発生し現在のトランザクションが終了します。 この時、SQLITE_CONSTRAINTの戻りコードでSQLが異常終了します。 トランザクションが有効でない場合に制約違反が発生するとABORTと同じように動作します。
このメソッドは制約違反の場合はアルゴリズムよって挙動が異なるので注意が必要です。
BookDAOにinsertWithOnConflictを実装する。
今回はSQLiteDatabase.CONFLICT_REPLACEを使用して制約違反の原因のデータとこれから挿入するデータの置き換えを行います。
    public static long insertWithOnConflict(Context context , BookDTO bookDTO){
        SQLiteDatabase sqLiteDatabase = getSqLiteDatabase(context,true);
        long rowId = sqLiteDatabase.insertWithOnConflict(
                LoadToSQLiteMasterContract.Book.TABLE_NAME ,
                COLUMN_NAME_SUBTITLE,
                convertContentValues(bookDTO),
                SQLiteDatabase.CONFLICT_REPLACE
        );
        return rowId;
    }

SQLiteActivityにBookDAO.insertWithOnConflictを使用するinsertWithOnConflictBookを実装する。
このinsertWithOnConflictBookを二回呼び出すことで制約エラーを発生させます。
  @Override
  protected void onResume() {
      super.onResume();
      insertWithOnConflictBook();
  }

  private void insertWithOnConflictBook() {
      BookDTO book = new BookDTO();
      book.setId(1);
      book.setTitle("SQLiteの勉強本");
      book.setSubtitle("サブタイトル : " + new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(Calendar.getInstance().getTime()));
      long rowId = BookDAO.insertWithOnConflict(getApplicationContext() , book);
      Toast.makeText(this, "rowId:" + rowId , Toast.LENGTH_SHORT).show();
  }