page.title=Salvando dados em bancos de dados do SQL page.tags=armazenamento de dados helpoutsWidget=true trainingnavtop=true @jd:body

Esta lição ensina a

  1. Definir um esquema e contrato
  2. Criar um banco de dados usando de um SQL Helper
  3. Colocar informações no banco de dados
  4. Ler informações de um banco de dados
  5. Excluir informações de um banco de dados
  6. Atualizar um banco de dados

Leia também

Salvar dados em um banco de dados é ideal para dados que se repetem ou estruturados, como informações de contato. Esta lição assume que você esteja familiarizado com bancos de dados do SQL em gera, e o ajuda a começar a trabalhar com bancos de dados do SQLite no Android. As APIs necessárias para usar um banco de dados no Android estão disponíveis no pacote {@link android.database.sqlite}.

Definir um esquema e contrato

Um dos princípios mais importantes de bancos de dados do SQL é o esquema: uma declaração formal de como o banco de dados é organizado. O esquema é refletido nas declarações SQL usadas na criação do banco de dados. É aconselhável criar uma classe de acompanhamento, conhecida como classe de contrato, que especifica claramente o layout do esquema de forma sistemática e autodocumentada.

Uma classe de contrato é o contêiner das constantes que definem nomes para URIs, tabelas e colunas. A classe de contrato permite usar as mesmas constantes em outras classes no mesmo pacote. Permite que você altere o nome da coluna em um local e que a mudança se propague pelos seus códigos.

Uma boa forma de organizar uma classe de contrato é colocar definições que sejam globais para todo o banco de dados no nível raiz da classe. Crie uma classe interna para cada tabela que enumera suas colunas.

Observação: implementando a interface {@link android.provider.BaseColumns}, sua classe interior pode herdar um campo-chave primário chamado {@code _ID} que algumas classes do Android, como adaptadores de cursor, esperam que você tenha. Não é uma obrigatório, mas pode ajudar para um trabalho mais harmonioso com o banco de dados no framework do Android.

Por exemplo, este trecho define o nome da tabela e das colunas para uma única tabela:

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";
        ...
    }
}

Criar um banco de dados usando de um SQL Helper

Uma vez definidos o visual dos bancos de dados, implemente métodos que criam e cuidam do banco de dados e das tabelas. Aqui estão algumas declarações comuns para criar e exclui a tabela:

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;

Da mesma forma você salva arquivos no armazenamento interno do dispositivo, o Android armazena seu banco de dados no espaço privado do disco associado ao aplicativo. Seus dados estão protegidos porque, por padrão, essa área não pode ser acessada por outros aplicativos.

Um conjunto de APIs está disponível na classe {@link android.database.sqlite.SQLiteOpenHelper}. Ao usar esta classe para obter referências para seu banco de dados, o sistema realiza operações de possível longa execução para criar e atualizar o banco de dados apenas quando necessário e não durante a inicialização do aplicativo. Basta chamar {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} ou {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}.

Observação: devido à possibilidade de serem de longa execução, certifique-se que chamar {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} ou {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} em um thread de segundo plano, como {@link android.os.AsyncTask} ou {@link android.app.IntentService}.

Para usar {@link android.database.sqlite.SQLiteOpenHelper}, crie uma subclasse que substitua os métodos de retorno de chamada {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}, {@link android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} e {@link android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()}. Também é possível implementar {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()}, mas não é obrigatório.

Por exemplo, esta é uma implementação de {@link android.database.sqlite.SQLiteOpenHelper} que utiliza alguns dos comandos exibidos abaixo:

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);
    }
}

Para acessar seu banco de dados, instancie sua subclasse de {@link android.database.sqlite.SQLiteOpenHelper}:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Colocar informações no banco de dados

Coloque dados no banco de dados transmitindo um objeto {@link android.content.ContentValues} para o método {@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);

Este primeiro argumento para {@link android.database.sqlite.SQLiteDatabase#insert insert()} é apenas o nome da tabela. O segundo argumento fornece o nome de uma coluna em que o framework pode inserir NULL caso o {@link android.content.ContentValues} esteja vazio (se você definir como {@code "null"}, o framework não inserirá uma linha quando não houver valores).

Ler informações de um banco de dados

Para ler de um banco de dados, utilize o método {@link android.database.sqlite.SQLiteDatabase#query query()} , transmitindo seus critérios de seleção e colunas desejadas. O método combina elementos de {@link android.database.sqlite.SQLiteDatabase#insert insert()} e {@link android.database.sqlite.SQLiteDatabase#update update()}, exceto que a lista da coluna define os dados que serão analisados e não os dados a serem inseridos. Os resultados da consulta são retornados em um objeto {@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
    );

Para ver uma linha no cursor, utilize um dos métodos de movimento {@link android.database.Cursor}, que sempre deverão ser chamados antes de começar a ler valores. Geralmente, deve-se iniciar chamando {@link android.database.Cursor#moveToFirst}, que coloca a “posição leitura” na primeira entrada nos resultados. Para cada linha, você pode ler um valor de coluna chamando um dos métodos GET {@link android.database.Cursor}, como {@link android.database.Cursor#getString getString()} ou {@link android.database.Cursor#getLong getLong()}. Para cada um dos métodos GET, você deve transmitir a posição de índice da coluna desejada, que pode ser obtida chamando {@link android.database.Cursor#getColumnIndex getColumnIndex()} ou {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()}. Por exemplo:

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

Excluir informações de um banco de dados

Para excluir linhas de uma tabela, forneça os critérios de seleção que as identifique. A API do banco de dados oferece um mecanismo para criar critérios de seleção que protegem contra injeção do SQL. O mecanismo divide a especificação da seleção em uma cláusula e argumentos de seleção. A cláusula define a coluna a se olhar e também permite combinar testes de coluna. Os argumentos são valores para testes comparativos que são dependentes dentro de uma cláusula. Como o resultado não é tratado da mesma forma que uma declaração SQL comum, ele fica imune à injeção de 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);

Atualizar um banco de dados

Quando precisar alterar um subconjunto dos valores de seu banco de dados, utilize o método {@link android.database.sqlite.SQLiteDatabase#update update()}.

A atualização da tabela combina a sintaxe de valores do conteúdo de {@link android.database.sqlite.SQLiteDatabase#insert insert()} com a sintaxe {@code where} de {@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);