Salvar dados em um banco de dados é ideal para dados estruturados ou que se repetem,
como informações de contato. Esta lição supõe que você esteja
familiarizado com bancos de dados SQL em geral e ajuda a começar a trabalhar com bancos de dados
SQLite no Android. As APIs necessárias para usar um banco de dados
no Android estão disponíveis no pacote android.database.sqlite.
Definir um esquema e um contrato
Um dos princípios mais importantes de bancos de dados 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 explicitamente 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. Isso permite que você altere o nome da coluna em um local e que a mudança se propague por todo o código.
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 as colunas.
Observação: Ao implementar a interface BaseColumns, sua classe interna pode herdar
um campo-chave principal chamado _ID que algumas classes de Android, como adaptadores de cursor,
esperarão que ela tenha. Ela não é obrigatória, mas pode ajudar o banco de dados a trabalhar de forma mais harmoniosa
com a estrutura de trabalho do Android.
Por exemplo, este snippet define os nomes da tabela e das colunas para uma única tabela:
public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// make the constructor private.
private FeedReaderContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = "entry";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
}
}
Criar um banco de dados usando um SQL Helper
Uma vez definida a estrutura do banco de dados, implemente métodos que criam e mantêm o banco de dados e as tabelas. Eis algumas declarações comuns para criar e excluir uma 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_TITLE + TEXT_TYPE + COMMA_SEP +
FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + " )";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
Da mesma forma como você salva arquivos no armazenamento interno do dispositivo, o Android armazena o 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 útil de APIs está disponível na classe SQLiteOpenHelper.
Quando você usa essa classe para obter referências ao seu banco de dados, o sistema
executa as operações potencialmente
de longa duração de criação e atualização do banco de dados somente quando
necessário e não durante a inicialização do aplicativo. Você precisa apenas chamar
getWritableDatabase()
ou getReadableDatabase().
Observação: Como podem ser de longa duração,
certifique-se de chamar getWritableDatabase() ou getReadableDatabase() em um encadeamento de segundo plano,
como com AsyncTask ou IntentService.
Para usar SQLiteOpenHelper, crie uma subclasse que
modifique os métodos de retorno de chamada onCreate(), onUpgrade() e onOpen(). Também é possível
implementar onDowngrade(),
mas não é obrigatório.
Por exemplo, esta é uma implementação de SQLiteOpenHelper que usa alguns dos comandos exibidos acima:
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 o banco de dados, instancie a subclasse de SQLiteOpenHelper:
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
Colocar informações no banco de dados
Insira dados no banco de dados passando um objeto ContentValues
para o método 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_TITLE, title); values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle); // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
O primeiro argumento para insert()
é simplesmente o nome da tabela.
O segundo argumento diz à estrutura o que fazer caso o
ContentValues esteja vazio (ou seja, você não
put nenhum valor).
Se você especificar o nome de uma coluna, a estrutura insere uma linha e
determina o valor dessa coluna como nulo. Se você especificar null, como nesta amostra de
código, a estrutura não insere uma linha quando não houver valores.
Ler informações de um banco de dados
Para ler de um banco de dados, use o método query()
passando os critérios de seleção e as colunas desejadas.
O método combina elementos de insert()
e update(), exceto que a lista de colunas
define os dados que você deseja recuperar, em vez de os dados a serem inseridos. Os resultados da consulta
são retornados em um objeto 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_SUBTITLE
};
// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_SUBTITLE + " 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, use um dos métodos de movimento Cursor,
que sempre deverão ser chamados antes de começar a ler valores. Geralmente, deve-se iniciar
chamando moveToFirst(), que coloca a “posição de leitura” na
primeira entrada nos resultados. Para cada linha, você pode ler o valor de uma coluna chamando um dos métodos GET
Cursor, como getString() ou getLong(). Para cada um dos métodos GET,
você deve passar a posição de índice da coluna desejada, que pode ser obtida chamando
getColumnIndex() ou
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 identifiquem. 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 de seleção e argumentos de seleção. A cláusula define a coluna a verificar e permite combinar testes de coluna. Os argumentos são valores para testes comparativos que são vinculados dentro da cláusula. Como o resultado não é processado da mesma forma que uma declaração SQL comum, ele é imune à injeção de SQL.
// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
Atualizar um banco de dados
Quando precisar modificar um subconjunto dos valores de seu banco de dados, use o método update().
Atualizar a tabela combina a sintaxe valores de conteúdo de insert() com a where sintaxe
de 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 title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyTitle" };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);