
Olá povo,
Entre as muitas coisas legais que o Android trouxe, o suporte nativo ao banco de dados SQLite foi uma das mais importantes, comercialmente falando. Com ele podemos utilizar os bons e famosos comandos SQL (SELECT, INSERT, UPDATE, DELETE, etc) no mundo mobile. Quem programou em J2ME sabe o quão complicado é implementar um repositório "digno" que suporte uma boa quantidade de registros nessa plataforma (não me falem de RMS... :)
Nesse POST, vou colocar trechos de código que utilizam esses conceitos e tentarei explicá-los. A parte da interface gráfica, vou explicar apenas o que for relevante para o uso com banco de dados.
Para criarmos um banco de dados SQLite, podemos:
- utilizar a ferramenta SQLite Expert Personal e colocar o arquivo do banco de dados na pasta /data/data/pacote_da_minha_aplicacao/databases;
- ou via código, utilizando uma classe "helper" do SQLite.
Utilizaremos a segunda abordagem, pois na vida real não queremos colocar esse arquivo manualmente no aparelho. Pois, caso seja necessária uma atualização, teremos que atualizar N aparelhos.
Mãos à obra! Vamos criar uma classe que herda de
android.database.sqlite.SQLiteOpenHelper. Ela será responsável por criar o banco quando a aplicação for instalada (método onCreate) e atualizá-lo para novas versões da aplicação (método onUpgrade).
public class SQLiteHelper extends SQLiteOpenHelper {
private String scriptCreate;
private String scriptDelete;
public SQLiteHelper(Context ctx, String nomeBd,
int versaoBanco, String scriptCreate,
String scriptDelete) {
super(ctx, nomeBd, null, versaoBanco);
this.scriptCreate = scriptCreate;
this.scriptDelete = scriptDelete;
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(scriptCreate);
}
public void onUpgrade(SQLiteDatabase db,
int oldVersion, int newVersion) {
db.execSQL(scriptDelete);
onCreate(db);
}
}
Agora vamos criar a classe que realizará as operações de abrir, selecionar, inserir, atualizar e excluir registos no banco de dados:
public class RepositorioCarro {
private SQLiteDatabase db;
private SQLiteHelper dbHelper;
private static final String SCRIPT_DB_DELETE =
"DROP TABLE IF EXISTS carros";
private static final String SCRIPT_DB_CREATE =
"create table carros (_id integer primary "+
"key autoincrement, nome text not null, "+
"placa text not null, ano text not null);";
public RepositorioCarro (Context ctx){
dbHelper = new SQLiteHelper(ctx, "curso", 1,
SCRIPT_DB_CREATE, SCRIPT_DB_DELETE);
}
private long inserir(Carro c){
ContentValues cv = new ContentValues();
cv.put("nome", c.getNome())
cv.put("placa", c.getPlaca());
cv.put("ano", c.getAno());
db = dbHelper.getWritableDatabase();
long id = db.insert("carros", null, cv);
db.close();
return id;
}
private long atualizar(Carro c){
ContentValues cv = new ContentValues();
cv.put("nome", c.getNome());
cv.put("placa", c.getPlaca());
cv.put("ano", c.getAno());
db = dbHelper.getWritableDatabase();
long rows = db.update("carros", cv, "_id = ?",
new String[]{ String.valueOf(c.getId())});
db.close();
return rows; // qtde. de linhas afetadas
}
public int excluir(int id){
db = dbHelper.getWritableDatabase();
int rows = db.delete("carros", "_id = ?",
new String[]{ String.valueOf(id) });
return rows; // qtde. de linhas afetadas
}
public List<Carro> buscarCarroPorNome(String nome){
List<Carro> lista = new ArrayList<Carro>();
String[] columns = new String[]{
"_id", "nome", "placa", "ano"};
String[] args = new String[]{nome+"%"};
db = dbHelper.getWritableDatabase();
Cursor c = db.query("carros", columns,
"nome like ?", args, null, null, "nome");
c.moveToFirst();
while(!c.isAfterLast()){
Carro carro = fillCarro(c);
lista.add(carro);
c.moveToNext();
}
c.close();
db.close();
return lista;
}
private Carro fillCarro(Cursor c) {
Carro carro = new Carro();
carro.setId((int)c.getLong(0));
carro.setNome(c.getString(1));
carro.setPlaca(c.getString(2));
carro.setAno(c.getString(3));
return carro;
}
}
Como vocês podem ver, estou usando uma classe Carro também. Não vou listá-la aqui por se tratar de um
POJO com os campos: id, nome, placa e ano do veículo.
Na classe acima, o construtor recebe uma referência do contexto da aplicação. Ela é repassada para nossa classe SQLHelper, que por sua vez, se encarregará de criar o nosso banco de dados se ele não existir. A classe
android.database.sqlite.SQLiteOpenHelper (superclasse do nosso helper) tem o método getWritableDatabase() que retorna uma instância da classe
android.database.sqlite.SQLiteDatabase, que será nosso canal de comunicação com o banco de dados.
As operações de inserir e atualizar utilizam: uma string que representa o nome da tabela no banco de dados; e um objeto da classe ContentValues, que nada mais é do que um conjunto par/valor com os parâmetros do comando SQL. Mas notem que o método update recebe ainda uma String que representa a cláusula WHERE do comando UPDATE (do SQL), bem como os parâmetros da mesma, que estão mapeados via caracter '?'.
"Por tabela", o método excluir vocês já devem ter entendido :)
O último método é o buscarCarroPorNome(String). Toda consulta SQL, retorna um objeto do tipo Cursor. Ele é bem parecido com o ResultSet do JDBC. Os parâmetros do método query são, respectivamente: String nome da tabela, String[] colunasDaTabela, String clausulaWhere, String[] paramsWhere, String groupBy, String having, String orderBy.
Para exibir os dados, precisaremos criar um Adapter, que converterá um Carro em uma linha de uma lista. Crie o arquivo de layout list_item.xml e coloque-o na pasta res/layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_height="wrap_content"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:id="@+id/carroRow">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txtNome"
android:layout_weight="1"
android:textSize="20dp"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txtAno"
android:layout_marginRight="10px"/>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/txtPlaca"
android:layout_marginRight="10px"/>
</LinearLayout>
E agora, vamos criar nosso adapter.
public class CarroListAdapter extends BaseAdapter {
private Context context;
private List<Carro> lista;
public CarroListAdapter(Context ctx,
List<Carro> carros) {
context = ctx;
lista = carros;
}
public int getCount() {
return lista.size();
}
public Object getItem(int arg0) {
return lista.get(arg0);
}
public long getItemId(int arg0) {
return lista.get(arg0).getId();
}
public View getView(int pos, View convertView,
ViewGroup parent) {
Carro c = lista.get(pos);
LayoutInflater inflater =
(LayoutInflater)context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(
R.layout.list_item, null);
((TextView)view.findViewById(R.id.txtNome)).
setText(c.getNome());
((TextView)view.findViewById(R.id.txtAno)).
setText(c.getAno());
((TextView)view.findViewById(R.id.txtPlaca)).
setText(c.getPlaca());
return view;
}
}
Crie uma classe que herde de ListActivity e no onCreate, digite o seguinte código:
RepositorioCarro carrosDB = new RepositorCarro(this);
List<Carro>lista = carrosDB.buscarCarroPorNome("");
setAdapter(new CarroListAdapter(this, lista));
Para utilizar os demais métodos é facinho, não é? Basta criar objetos do tipo Carro, no qual os valores serão preenchidos por widgets (componentes de UI) e passá-los como parâmetros para os métodos do RepositorioCarro.
Espero que tenham entendido e gostado :)
Editado em 03/11/2011
Sempre que fizermos uma operação no banco de dados é importante que encerremos a conexão com ele. Tinha faltado isso nesse post, e agora lembrei de corrirgir.
4br4ç05
nglauber