page.title=Сохранение данных в базах данных SQL page.tags=хранение данных helpoutsWidget=true trainingnavtop=true @jd:body

Содержание этого урока

  1. Определение схемы и контракта
  2. Создание базы данных с помощью SQL Helper
  3. Размещение информации в базе данных
  4. Чтение информации из базы данных
  5. Удаление информации из базы данных
  6. Обновление базы данных

См. также:

Сохранение данных в базе идеально подходит для повторяющихся и структурированных данных, таких как контактная информация. В этом учебном курсе предполагается, что вы владеете общими знаниями о базах данных SQL, и он поможет вам начать работать с базами данных SQLite на платформе Android. API-интерфейсы, необходимые для использования базы данных на платформе Android, доступны в составе пакета {@link android.database.sqlite}.

Определение схемы и контракта

Одним из основных элементов баз данных SQL является схема, которая представляет собой формальную декларацию способа организации базы данных. Схема отражается в выражениях SQL, используемых для создания базы данных. Для вас может оказаться полезным создать сопутствующий класс (класс-контракт), явно указывающий структуру схемы систематическим и самодокументирующим способом.

Класс-контракт представляет собой контейнер, определяющий имена для URI-адресов, таблиц и столбцов. Класс-контракт позволяет использовать одни и те же постоянные значения во всех других классах этого же пакета. Таким образом, при изменении имени столбца в одном месте это изменение применяется во всем коде.

Для удобства организации класс-контракта стоит поместить глобальные определения базы данных на корневой уровень класса. Затем нужно создать внутренний класс для каждой таблицы, производящей нумерацию столбцов.

Примечание. За счет реализации интерфейса {@link android.provider.BaseColumns} внутренний класс может наследовать поле первичного ключа {@code _ID}, наличия которого ожидают от него некоторые классы Android (например, адаптеры курсора). Это не является обязательным условием, однако может помочь обеспечить гармоничную работу вашей базы данных в инфраструктуре Android.

Например, в этом фрагменте кода определяются имя таблицы и имена столбцов для одной таблицы:

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // give it an empty constructor.
    public FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
}

Создание базы данных с помощью SQL Helper

После определения внешнего вида базы данных следует реализовать методы создания и обслуживания базы данных и таблиц. Вот некоторые типичные выражения для создания и удаления таблиц:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // Any other options for the CREATE command
    " )";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

Как и при сохранении файлов во внутренней памяти устройства, Android сохраняет вашу базу данных в закрытой области диска, связанной с приложением Эти данные защищены, потому что эта область по умолчанию недоступна другим приложениям.

Полезный набор API-интерфейсов содержится в классе {@link android.database.sqlite.SQLiteOpenHelper}. Если вы используете этот класс для получения ссылок на свою базу данных, система выполняет потенциально долговременные операции создания и обновления базы данных только тогда, когда это необходимо, а не при запуске приложения. Для этого нужно использовать вызов {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} или {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.

Примечание. Поскольку операции могут выполняться длительное время, вызывайте {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} или {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} в фоновом потоке, например с {@link android.os.AsyncTask} или {@link android.app.IntentService}.

Для использования {@link android.database.sqlite.SQLiteOpenHelper} создайте подкласс, заменяющий методы вызова {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} и {@link android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()}. Также вы можете использовать {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, но это не требуется.

Например, вот реализация {@link android.database.sqlite.SQLiteOpenHelper}, при которой используются некоторые из приведенных выше команд:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

Для получения доступа к базе данных создайте экземпляр подкласса {@link android.database.sqlite.SQLiteOpenHelper}:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Размещение информации в базе данных

Добавьте данные в базу данных, передав объект {@link android.content.ContentValues} в метод {@link android.database.sqlite.SQLiteDatabase#insert insert()}:

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
         FeedEntry.TABLE_NAME,
         FeedEntry.COLUMN_NAME_NULLABLE,
         values);

Первый аргумент {@link android.database.sqlite.SQLiteDatabase#insert insert()} представляет собой просто название таблицы. Второй аргумент указывает имя столбца, в который инфраструктура вставляет значение NULL, если {@link android.content.ContentValues} является пустым (если вместо этого указать {@code "null"}, то инфраструктура не будет вставлять строку в случае отсутствия значений).

Чтение информации из базы данных

Для чтения из базы данных используйте метод {@link android.database.sqlite.SQLiteDatabase#query query()} с передачей критериев выделения и желаемых столбцов. Метод сочетает элементы {@link android.database.sqlite.SQLiteDatabase#insert insert()} и {@link android.database.sqlite.SQLiteDatabase#update update()}, за исключением того, что список столбцов определяет данные, которые вы хотите получить, а не данные для вставки. Результаты запроса возвращаются в объекте {@link android.database.Cursor}.

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

Чтобы посмотреть на строку в месте курсора, используйте один из методов перемещения {@link android.database.Cursor}, которые всегда нужно вызывать перед считыванием значений. Обычно следует начинать с вызова {@link android.database.Cursor#moveToFirst}, который помещает "позицию чтения" на первую запись в результатах. Для каждой строки значение столбца можно прочитать, вызвав один из методов {@link android.database.Cursor} get, например {@link android.database.Cursor#getString getString()} или {@link android.database.Cursor#getLong getLong()}. Для каждого из методов get вы должны передать указатель желаемого столбца, который может вызвать {@link android.database.Cursor#getColumnIndex getColumnIndex()} или {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. Например:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

Удаление информации из базы данных

Для удаления строк из таблицы нужно указать критерии выделения, идентифицирующие строки. API-интерфейс базы данных обеспечивает механизм для создания критериев выделения, предоставляющий защиту от внедрения SQL-кода. Механизм делит спецификацию выбора на предложение выбора и аргументы выбора. Предложение определяет столбцы для рассмотрения, а также позволяет объединять операции тестирования столбцов. Аргументы представляют собой значения для тестирования, которые привязаны к пункту. Поскольку результат обрабатывается не как обычные выражения SQL, он защищен от внедрения SQL-кода.

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

Обновление базы данных

При необходимости изменить набор значений базы данных используйте метод {@link android.database.sqlite.SQLiteDatabase#update update()}.

Обновление таблицы сочетает значения синтаксиса {@link android.database.sqlite.SQLiteDatabase#insert insert()} и синтаксиса {@code where} для {@link android.database.sqlite.SQLiteDatabase#delete delete()}.

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);